Come funziona effettivamente il costruttore const?

Ho notato che è ansible creare un costruttore const in Dart. Nella documentazione, dice che la parola const è usata per denotare qualcosa una costante di tempo di compilazione.

Mi stavo chiedendo cosa succede quando uso un costruttore const per creare un object. È simile a un object immutabile che è sempre lo stesso e disponibile al momento della compilazione? Come funziona il concetto di costruttore di const ? In che modo un costruttore const è diverso da un costruttore “normale”?

Il costruttore Const crea un’istanza “canonizzata”.

Cioè, tutte le espressioni costanti iniziano canonicalizzate, e più tardi questi simboli “canonizzati” sono usati per riconoscere l’equivalenza di queste costanti.

canonicalization:

Un processo per la conversione di dati che ha più di una ansible rappresentazione in una rappresentazione canonica “standard”. Ciò può essere fatto per confrontare diverse rappresentazioni per l’equivalenza, per contare il numero di strutture di dati distinte, per migliorare l’efficienza di vari algoritmi eliminando calcoli ripetuti o per consentire di imporre un ordine di ordinamento significativo.


Ciò significa che le espressioni const come const Foo(1, 1) possono rappresentare qualsiasi forma utilizzabile per il confronto nella macchina virtuale.

La VM deve solo prendere in considerazione il tipo di valore e gli argomenti nell’ordine in cui si verificano in questa espressione const. E, naturalmente, sono ridotti per l’ottimizzazione.

Costanti con gli stessi valori canonizzati:

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

Costanti con diversi valori canonizzati (poiché le firme differiscono):

 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 

Le costanti non vengono ricreate ogni volta. Sono canonicalizzati al momento della compilazione e memorizzati in speciali tabelle di ricerca (dove sono sottoposti a hashing con le loro firme canoniche) da cui vengono successivamente riutilizzati.

PS

Il modulo #Foo#int#1#int#1 utilizzato in questi esempi viene utilizzato solo a scopo di confronto e non è una vera forma di canonicalizzazione (rappresentazione) in Dart VM;

Ma la vera forma di canonizzazione deve essere una rappresentazione canonica “standard”.

Trovo che la risposta di Lasse sul blog di Chris Storms sia una grande spiegazione.

Dart Constant Constructors

Spero che a loro non dispiaccia che copio il contenuto.

Questa è una buona spiegazione dei campi finali, ma in realtà non spiega i costruttori di cost. Niente in questi esempi in realtà usa che i costruttori siano costruttori const. Qualsiasi class può avere campi finali, costruttori const o no.

Un campo in Dart è in realtà una posizione di archiviazione anonima combinata con un getter e setter creato automaticamente che legge e aggiorna la memoria e può anche essere inizializzato nell’elenco di inizializzazione di un costruttore.

Un campo finale è lo stesso, solo senza setter, quindi l’unico modo per impostarne il valore è nella lista di inizializzazione del costruttore, e non c’è modo di cambiare il valore dopo quello – da qui il “finale”.

Il punto dei costruttori const non è quello di inizializzare i campi finali, qualsiasi costruttore generativo può farlo. Il punto è creare valori costanti in fase di compilazione: oggetti in cui tutti i valori di campo sono già noti al momento della compilazione, senza eseguire alcuna istruzione.

Ciò pone alcune restrizioni alla class e al costruttore. Un costruttore const non può avere un corpo (nessuna istruzione eseguita!) E la sua class non deve avere campi non finali (il valore che “conosciamo” al momento della compilazione non può essere in grado di cambiare in seguito). L’elenco di inizializzazione deve anche solo inizializzare i campi con altre costanti in fase di compilazione, in modo che i lati di destra siano limitati a “espressioni costanti in fase di compilazione” [1]. E deve essere preceduto da “const” – altrimenti si ottiene semplicemente un normale costruttore che soddisfa tali requisiti. Va perfettamente bene, non è solo un costruttore di const.

Per utilizzare un costruttore const per creare effettivamente un object costante in fase di compilazione, sostituisci “nuovo” con “const” in un’espressione “nuova”. Puoi ancora usare “new” con un const-constructor e continuerà a creare un object, ma sarà solo un normale object nuovo, non un valore costante in fase di compilazione. Cioè: un costruttore const può anche essere usato come un normale costruttore per creare oggetti in runtime, oltre a creare oggetti costanti in fase di compilazione in fase di compilazione.

Quindi, ad esempio:

 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! } 

Le costanti di tempo di compilazione sono canonizzate. Ciò significa che non importa quante volte scrivi “const Point (0,0)”, crei solo un object. Potrebbe essere utile, ma non tanto quanto sembrerebbe, dal momento che puoi semplicemente creare una variabile const per contenere il valore e usare invece la variabile.

Quindi, quali sono le costanti in fase di compilazione valide comunque?

  • Sono utili per le enumerazioni.
  • È ansible utilizzare valori costanti in fase di compilazione nei casi di switch.
  • Sono usati come annotazioni.

Le costanti in tempo di compilazione erano più importanti prima che Dart passasse alle variabili di inizializzazione pigramente. Prima di ciò, si poteva solo dichiarare una variabile globale inizializzata come “var x = pippo;” se “pippo” era una costante in fase di compilazione. Senza questo requisito, è ansible scrivere la maggior parte dei programmi senza utilizzare alcun object const

Quindi, breve riassunto: i costruttori di Const sono solo per creare valori costanti in fase di compilazione.

/ L

[1] O davvero: “Potenzialmente espressioni della costante di compilazione” perché potrebbe anche fare riferimento ai parametri del costruttore. [2] Quindi sì, una class può avere contemporaneamente costruttori const e non-const.

Un esempio dimostrativo che l’istanza const decide davvero in base al campo finale.
E in questo caso, non può essere previsto in fase di compilazione.

 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) }); } 

Ora la freccia lo controllerà.

Analisi del dardo:

[dart] Imansible definire il costruttore ‘const’ perché il campo ‘j’ è inizializzato con un valore non costante

Errore di runtime:

/main.dart ‘: errore: riga 5 pos 17: espressione non è una costante di tempo di compilazione finale int j = new DateTime.now (). millisecond;