कॉन्स्ट कंस्ट्रक्टर वास्तव में कैसे काम करता है?


112

मैंने देखा है कि डार्ट में एक कॉन्स्ट कंस्ट्रक्टर बनाना संभव है। प्रलेखन में, यह कहता है कि constशब्द का उपयोग कुछ संकलन समय स्थिर करने के लिए किया जाता है।

मैं सोच रहा था कि क्या होता है जब मैं constएक ऑब्जेक्ट बनाने के लिए एक कंस्ट्रक्टर का उपयोग करता हूं । क्या यह एक अपरिवर्तनीय वस्तु की तरह है जो हमेशा एक ही है और संकलन के समय उपलब्ध है? constकंस्ट्रक्टर की अवधारणा वास्तव में कैसे काम करती है? एक कैसे है स्थिरांक निर्माता एक से अलग नियमित निर्माता?

जवाबों:


78

कांस्ट कंस्ट्रक्टर "कैनोनिकलाइज़्ड" उदाहरण बनाता है।

यही है, सभी स्थिर अभिव्यक्ति विहित शुरू होती हैं, और बाद में इन "कैनोनिकलकृत" प्रतीकों का उपयोग इन स्थिरांक की समानता को पहचानने के लिए किया जाता है।

कैनॉनिकलाइज़ेशन:

डेटा को परिवर्तित करने की एक प्रक्रिया जिसमें "मानक" विहित प्रतिनिधित्व में एक से अधिक संभव प्रतिनिधित्व है। यह तुल्यता के लिए विभिन्न अभ्यावेदन की तुलना करने के लिए, अलग-अलग डेटा संरचनाओं की संख्या की गणना करने के लिए, बार-बार गणना को समाप्त करके विभिन्न एल्गोरिदम की दक्षता में सुधार करने के लिए, या एक सार्थक क्रमबद्ध क्रम को लागू करने के लिए संभव बनाने के लिए किया जा सकता है।


इसका मतलब है कि कॉन्स्टेंट एक्सप्रेशंस जैसे const Foo(1, 1)किसी भी प्रयोग करने योग्य रूप का प्रतिनिधित्व कर सकते हैं जो वर्चुअल मशीन की तुलना में उपयोगी है।

वीएम को केवल इस प्रकार की अभिव्यक्ति में होने वाले क्रम में मूल्य प्रकार और तर्कों को ध्यान में रखना होगा। और, ज़ाहिर है, वे अनुकूलन के लिए कम हो जाते हैं।

एक ही विहित मूल्यों के साथ निरंतर:

var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1

विभिन्न विहित मूल्यों के साथ स्थिरांक (क्योंकि हस्ताक्षर भिन्न होते हैं):

var foo3 = const Foo(1, 2); // $Foo$int$1$int$2
var foo4 = const Foo(1, 3); // $Foo$int$1$int$3

var baz1 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello
var baz2 = const Baz(const Foo(1, 1), "hello"); // $Baz$Foo$int$1$int$1$String$hello

कॉन्स्टेंट को हर बार नहीं बनाया जाता है। उन्हें संकलित समय पर विहित किया जाता है और विशेष लुकअप टेबल (जहां वे अपने विहित हस्ताक्षर द्वारा हैशेड में संग्रहीत किए जाते हैं) में संग्रहीत किए जाते हैं, जिनसे वे बाद में पुन: उपयोग किए जाते हैं।

पुनश्च

#Foo#int#1#int#1इन नमूनों में प्रयुक्त फॉर्म का उपयोग केवल तुलनात्मक उद्देश्यों के लिए किया जाता है और यह डार्ट वीएम में विहितीकरण (प्रतिनिधित्व) का वास्तविक रूप नहीं है;

लेकिन असली कैनोनिकलाइजेशन फॉर्म "मानक" कैनोनिकल प्रतिनिधित्व होना चाहिए।


80

मुझे क्रिस स्टॉर्म के ब्लॉग पर लास का जवाब बहुत अच्छा लगता है।

डार्ट कॉन्स्टेंट कंस्ट्रक्टर्स

मुझे आशा है कि उन्हें इस बात से कोई आपत्ति नहीं है कि मैं सामग्री की प्रतिलिपि बनाता हूं।

यह अंतिम क्षेत्रों की एक अच्छी व्याख्या है, लेकिन यह वास्तव में कॉन्स्ट कंस्ट्रक्टरों को नहीं समझाता है। इन उदाहरणों में कुछ भी वास्तव में उपयोग नहीं करता है कि निर्माणकर्ता निर्माणकर्ता हैं। किसी भी वर्ग में अंतिम क्षेत्र, कास्ट कंस्ट्रक्टर या नहीं हो सकते हैं।

डार्ट का एक क्षेत्र वास्तव में एक गुमनाम स्टोरेज लोकेशन है जो स्वचालित रूप से बनाए गए गेटटर और सेटर के साथ संयुक्त है जो स्टोरेज को पढ़ता और अपडेट करता है, और इसे एक कंस्ट्रक्टर की इनिशियलाइज़र सूची में आरंभीकृत भी किया जा सकता है।

अंतिम फ़ील्ड समान है, बस सेटर के बिना, इसलिए इसका मान सेट करने का एकमात्र तरीका कंस्ट्रक्टर इनिशियलाइज़र सूची में है, और उसके बाद मूल्य बदलने का कोई तरीका नहीं है - इसलिए "अंतिम"।

कॉन्स्ट कंस्ट्रक्टर्स की बात अंतिम फील्ड्स को इनिशियलाइज़ करने की नहीं है, कोई भी जेनेरिक कंस्ट्रक्टर ऐसा कर सकता है। बिंदु संकलन-समय स्थिर मान बनाने के लिए है: वे ऑब्जेक्ट जहां सभी फ़ील्ड मान पहले से ही संकलित समय पर ज्ञात हैं, बिना किसी कथन को निष्पादित किए।

यह वर्ग और निर्माता पर कुछ प्रतिबंध लगाता है। एक कंस्ट्रक्टर कंस्ट्रक्टर के पास बॉडी नहीं हो सकती है (कोई स्टेटमेंट निष्पादित नहीं किया गया है!) और उसकी क्लास में कोई नॉन-फाइनल फील्ड नहीं होना चाहिए (संकलन समय पर हम जो "जानते हैं" बाद में बदलने में सक्षम नहीं होना चाहिए)। प्रारंभिक सूची को केवल अन्य संकलन-समय स्थिरांक के लिए फ़ील्ड्स को इनिशियलाइज़ करना होगा, इसलिए राइट-हैंड साइड "कंपाइल-टाइम कंटीन्यूअस एक्सप्रेशंस" [1] तक सीमित हैं। और इसे "कॉन्स्ट" के साथ उपसर्ग करना होगा - अन्यथा आपको बस एक सामान्य निर्माता मिलता है जो उन आवश्यकताओं को पूरा करने के लिए होता है। यह पूरी तरह से ठीक है, यह सिर्फ एक कंस्ट्रक्टर नहीं है।

वास्तव में एक संकलन-समय स्थिर ऑब्जेक्ट बनाने के लिए एक कॉन्स्ट कंस्ट्रक्टर का उपयोग करने के लिए, आप फिर "कॉन्स्ट" के साथ "न्यू" को "न्यू" -प्रिक्रिया में बदल देते हैं। आप अभी भी एक कंस्ट्रक्टर के साथ "नए" का उपयोग कर सकते हैं, और यह अभी भी एक ऑब्जेक्ट बनाएगा, लेकिन यह केवल एक सामान्य नई ऑब्जेक्ट होगा, न कि एक संकलन-समय निरंतर मूल्य। वह यह है: एक कॉन्स्ट कंस्ट्रक्टर को रन टाइम पर ऑब्जेक्ट्स बनाने के लिए एक सामान्य कंस्ट्रक्टर के रूप में भी इस्तेमाल किया जा सकता है, साथ ही कंपाइल-टाइम कंटिन्यू ऑब्जेक्ट्स को कॉम्पीलेशन टाइम पर बनाया जा सकता है।

तो, एक उदाहरण के रूप में:

class Point { 
  static final Point ORIGIN = const Point(0, 0); 
  final int x; 
  final int y; 
  const Point(this.x, this.y);
  Point.clone(Point other): x = other.x, y = other.y; //[2] 
}

main() { 
  // Assign compile-time constant to p0. 
  Point p0 = Point.ORIGIN; 
  // Create new point using const constructor. 
  Point p1 = new Point(0, 0); 
  // Create new point using non-const constructor.
  Point p2 = new Point.clone(p0); 
  // Assign (the same) compile-time constant to p3. 
  Point p3 = const Point(0, 0); 
  print(identical(p0, p1)); // false 
  print(identical(p0, p2)); // false 
  print(identical(p0, p3)); // true! 
}

संकलन-समय स्थिरांक को विहित किया जाता है। इसका मतलब है कि आप कितनी बार "कॉन्स्ट पॉइंट (0,0)" लिखते हैं, आप केवल एक ऑब्जेक्ट बनाते हैं। यह उपयोगी हो सकता है - लेकिन उतना नहीं जितना यह प्रतीत होता है, क्योंकि आप मूल्य को पकड़ने के लिए एक कांस्टेबल चर बना सकते हैं और इसके बजाय चर का उपयोग कर सकते हैं।

तो, क्या संकलन-समय स्थिरांक वैसे भी अच्छे हैं?

  • वे एनम के लिए उपयोगी हैं।
  • स्विच मामलों में आप संकलन-समय निरंतर मान का उपयोग कर सकते हैं।
  • उनका उपयोग एनोटेशन के रूप में किया जाता है।

डैज़ल को वैरिएबल इनिशियलाइज़ करने के लिए स्विच करने से पहले कंपाइल-टाइम कॉन्स्टेंट अधिक महत्वपूर्ण हुआ करते थे। इससे पहले, आप केवल "var x = foo;" जैसा एक आरंभिक वैश्विक चर घोषित कर सकते थे; यदि "फू" एक संकलन-समय स्थिर था। उस आवश्यकता के बिना, अधिकांश कार्यक्रमों को किसी भी कास्ट ऑब्जेक्ट का उपयोग किए बिना लिखा जा सकता है

इसलिए, संक्षिप्त सारांश: कांस्ट कंस्ट्रक्टर केवल संकलन-समय स्थिर मान बनाने के लिए हैं।

/ एल

[१] या वास्तव में: "संभावित रूप से संकलन-समय स्थिर अभिव्यक्ति", क्योंकि यह निर्माण मापदंडों को भी संदर्भित कर सकता है। [२] तो हाँ, एक कक्षा में एक ही समय में कब्ज और गैर-कास्ट कंस्ट्रक्टर दोनों हो सकते हैं।

इस विषय पर कुछ रोचक टिप्पणियों के साथ https://github.com/dart-lang/sdk/issues/36079 में भी चर्चा की गई ।


AFAIK कास्ट और फाइनल अधिक अनुकूलित जेएस उत्पन्न करने की अनुमति देता है।
गुंटर ज़ोचबॉयर

2
वे विधि हस्ताक्षर में डिफ़ॉल्ट-मान के लिए भी उपयोगी हैं।
फ्लोरियन लोइस्च

1
क्या कोई मुझे समझा सकता है कि यह रेखा कैसे काम करती है? Point.clone(Point other): x = other.x, y = other.y;
दक्ष गरगास

क्या हिस्सा अस्पष्ट है? यह संबंधित नहीं दिखता हैconst
गुंटर ज़ोचबॉयर

3
constमध्यम के अनुसार स्पंदन विगेट्स के लिए एक अच्छा प्रदर्शन जीत है। सब-ट्री और कॉल बिल्ड में विजेट () कीमती साइकिलों को बर्बाद करना विशेष रूप से यदि आपके निर्माण के तरीके भारी हैं। "
डेविड चैंडलर

8

बहुत अच्छी तरह से विस्तार से समझाया गया है, लेकिन उन उपयोगकर्ताओं के लिए जो वास्तव में एक कास्ट कंस्ट्रक्टर के उपयोग की तलाश कर रहे हैं

इसका उपयोग स्पंदन प्रदर्शन को बढ़ाने के लिए किया जाता है क्योंकि यह स्पंदन को केवल विगेट्स के पुनर्निर्माण में मदद करता है जिसे अपडेट किया जाना चाहिए। StateFulWidgets में setState () का उपयोग करते समय केवल उन घटकों को फिर से बनाया जाएगा जो नॉन कॉन्स्ट कंस्ट्रक्टर हैं

उदाहरण के साथ समझाया जा सकता है->

    class _MyWidgetState extends State<MyWidget> {

  String title = "Title";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: <Widget>[
          const Text("Text 1"),
          const Padding(
            padding: const EdgeInsets.all(8.0),
            child: const Text("Another Text widget"),
          ),
          const Text("Text 3"),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          setState(() => title = 'New Title');
        },
      ),
    );
  }
}

जैसा कि इस उदाहरण में, केवल टेक्स्ट शीर्षक को बदलना चाहिए, इसलिए केवल इस विजेट को फिर से बनाना चाहिए, इसलिए सभी अन्य विजेट्स को कॉन्स्ट कंस्ट्रक्टर के रूप में बढ़ाने से प्रदर्शन को बढ़ाने में मदद मिलेगी।


0

एक उदाहरण डेमो जो कि कास्ट इंस्टेंस वास्तव में अंतिम क्षेत्र द्वारा तय करता है।
और इस मामले में, यह संकलन-समय में भविष्यवाणी नहीं की जा सकती है।

import 'dart:async';

class Foo {
  final int i;
  final int j = new DateTime.now().millisecond;
  const Foo(i) : this.i = i ~/ 10;

  toString() => "Foo($i, $j)";
}



void main() {
  var f2 = const Foo(2);
  var f3 = const Foo(3);

  print("f2 == f3 : ${f2 == f3}"); // true
  print("f2 : $f2"); // f2 : Foo(0, 598)
  print("f3 : $f3"); // f3 : Foo(0, 598)

  new Future.value().then((_) {
    var f2i = const Foo(2);
    print("f2 == f2i : ${f2 == f2i}"); // false
    print("f2i : $f2i"); // f2i : Foo(0, 608)
  });
}

अब डार्ट इसकी जांच करेगा।

डार्ट विश्लेषण:

[डार्ट] 'कॉन्स्ट' कंस्ट्रक्टर को परिभाषित नहीं कर सकता क्योंकि फील्ड 'जे' को एक गैर-स्थिर मूल्य के साथ आरंभीकृत किया गया है

रनटाइम त्रुटि:

/main.dart ': त्रुटि: लाइन 5 पॉज़ 17: अभिव्यक्ति एक वैध संकलन-समय स्थिर अंतिम int j = new DateTime.now () नहीं है। मिलीसेकंड;

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.