web-dev-qa-db-fra.com

Comment fonctionne le constructeur const?

J'ai remarqué qu'il est possible de créer un constructeur const dans Dart. Dans la documentation, il est indiqué que const Word est utilisé pour désigner quelque chose d'une constante de temps de compilation. 

Je me demandais ce qui se passait lorsque j'utilisais un constructeur const pour créer un objet. Est-ce que cela ressemble à un objet immuable qui est toujours le même et disponible au moment de la compilation? Comment fonctionne le concept de constructeur const? En quoi un constructeur const est-il différent d'un constructeur "régulier"?

42
markovuksanovic

Le constructeur Const crée une instance "canonicalisée".

C'est-à-dire que toutes les expressions constantes commencent à être canonisées et que, plus tard, ces symboles "canonisés" sont utilisés pour reconnaître l'équivalence de ces constantes.

Canonicalisation:

Un processus de conversion de données ayant plusieurs représentations possibles en une représentation canonique "standard". Cela peut être fait pour comparer différentes représentations d'équivalence, compter le nombre de structures de données distinctes, pour améliorer l'efficacité de divers algorithmes en éliminant les calculs répétés ou pour permettre d'imposer un ordre de tri significatif.


Cela signifie que des expressions constantes telles que const Foo(1, 1) peuvent représenter n’importe quelle forme utilisable utile à la comparaison dans une machine virtuelle.

La VM doit uniquement prendre en compte le type de valeur et les arguments dans l'ordre dans lequel ils apparaissent dans cette expression const. Et, bien sûr, ils sont réduits pour optimisation.

Constantes avec les mêmes valeurs canonisées:

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

Constantes avec différentes valeurs canonisées (parce que les signatures diffèrent):

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

Les constantes ne sont pas recréées à chaque fois. Elles sont canonisées au moment de la compilation et stockées dans des tables de consultation spéciales (où elles sont hachées par leurs signatures canoniques) à partir desquelles elles sont ultérieurement réutilisées.

P.S.

Le formulaire #Foo#int#1#int#1 utilisé dans ces exemples est uniquement utilisé à des fins de comparaison et il ne s'agit pas d'une véritable forme de canonisation (représentation) dans Dart VM;

Mais la forme réelle de canonisation doit être une représentation canonique "standard".

36
mezoni

La réponse de Lasse sur le blog de Chris Storms est une excellente explication.

Constructeurs Dart Constant

J'espère que cela ne me dérange pas que je copie le contenu.

Ceci est une bonne explication des champs finaux, mais ce n’est pas vraiment expliquer les constructeurs const. Rien dans ces exemples n'utilise réellement que les constructeurs sont des constructeurs const. Toute classe peut avoir une finale champs, constructeurs const ou non. 

Un champ dans Dart est vraiment un emplacement de stockage anonyme associé à un getter et un setter créés automatiquement qui lisent et mettent à jour le stockage, et il peut également être initialisé dans l’initialiseur du constructeur liste. 

Un dernier champ est le même, juste sans le passeur, donc le seul moyen de définir sa valeur est dans la liste d'initialisation du constructeur, et il n'y a pas façon de changer la valeur après cela - d’où la "finale".

L’intérêt des constructeurs de const n’est pas d’initialiser les champs finaux, ni aucun constructeur génératif peut le faire. Le but est de créer valeurs constantes à la compilation: Objets dont toutes les valeurs de champ sont déjà connu au moment de la compilation, sans exécuter aucune instruction.

Cela impose certaines restrictions à la classe et au constructeur. Un const Le constructeur ne peut pas avoir un corps (pas d'instructions exécutées!) et sa classe ne doit avoir aucun champ non final (la valeur que nous "savons" lors de la compilation time ne doit pas pouvoir changer plus tard). La liste d'initialisation doit également initialisez uniquement les champs avec d’autres constantes au moment de la compilation, ainsi. les membres de droite sont limités à "expressions de constante de compilation" [1]. Et il doit être préfixé par "const" - sinon vous Il suffit d’obtenir un constructeur normal qui satisfasse ceux exigences. C’est parfait, ce n’est tout simplement pas un const constructeur.

Afin d'utiliser un constructeur const pour créer réellement un moment de compilation objet constant, vous remplacez "new" par "const" dans un "nouvelle" -expression. Vous pouvez toujours utiliser "new" avec un constructeur const, et il va toujours créer un objet, mais ce sera juste un nouveau .__ normal. objet, pas une valeur constante à la compilation. C'est-à-dire: A const Le constructeur peut également être utilisé comme constructeur normal pour créer des objets au moment de l'exécution, ainsi que la création d'objets constants au moment de la compilation à temps de compilation.

Donc, à titre d'exemple: 

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

Les constantes à la compilation sont canonisées. Cela signifie que peu importe comment Plusieurs fois, vous écrivez "const Point (0,0)", vous ne créez qu'un seul objet . Cela peut être utile - mais pas autant qu'il semblerait, puisque vous pouvez créez simplement une variable const pour contenir la valeur et utilisez la variable au lieu.

Alors, à quoi servent les constantes au moment de la compilation?

  • Ils sont utiles pour les enums. 
  • Vous pouvez utiliser des valeurs constantes au moment de la compilation dans les cas de commutation.
  • Ils sont utilisés comme annotations.

Les constantes au moment de la compilation étaient plus importantes avant que Dart ne change de système paresseusement initialiser les variables. Avant cela, vous ne pouviez déclarer que une variable globale initialisée du type "var x = foo;" si "foo" était un constante de compilation. Sans cette exigence, la plupart des programmes peuvent être écrit sans utiliser d'objets const

Donc, bref résumé: les constructeurs Const sont juste pour créer valeurs constantes au moment de la compilation.

/ L

[1] Ou en réalité: "Expressions constantes potentiellement au moment de la compilation" car il peut également faire référence aux paramètres du constructeur . [2] Donc, oui, une classe peut avoir à la fois des constructeurs const et non const.

29
Günter Zöchbauer

Un exemple de démonstration que l'instance const décide réellement par champ final.
Et dans ce cas, cela ne peut pas être prévu au moment de la compilation.

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

Maintenant, Dart va le vérifier.

Analyse de fléchettes:

[Dart] Impossible de définir le constructeur 'const' car le champ 'j' est initialisé avec une valeur non constante

Erreur d'exécution:

/main.Dart ': erreur: ligne 5 pos 17: expression n'est pas une constante de compilation valide final int j = nouveau DateTime.now (). millisecond;

0
Ticore Shih