const构造函数如何实际工作?

时间:2014-02-13 03:35:26

标签: dart

我注意到可以在Dart中创建一个const构造函数。在文档中,它说const字用于表示编译时间常量。

我想知道当我使用const构造函数创建对象时会发生什么。这是一个永远相同且在编译时可用的不可变对象吗? const构造函数的概念如何实际起作用? const构造函数与“常规”构造函数有什么不同?

6 个答案:

答案 0 :(得分:46)

Const构造函数创建一个“规范化”实例。

也就是说,所有常量表达式都开始规范化,后来这些“规范化”符号用于识别这些常量的等价性。

<强>规范化:

将具有多个可能表示的数据转换为“标准”规范表示的过程。这样做可以比较不同的等价表示,计算不同数据结构的数量,通过消除重复计算来提高各种算法的效率,或者可以强加有意义的排序顺序。


这意味着像const Foo(1, 1)这样的const表达式可以表示任何可用于在虚拟机中进行比较的可用表单。

VM只需要按照它们在此const表达式中出现的顺序考虑值类型和参数。当然,它们会被缩减以进行优化。

具有相同规范化值的常量:

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

每次都不会重建常量。它们在编译时被规范化,并存储在特殊的查找表中(它们通过规范的签名进行哈希处理),以后可以重用它们。

P.S。

这些示例中使用的表单#Foo#int#1#int#1仅用于比较目的,它不是Dart VM中规范化(表示)的真实形式;

但真正的规范化形式必须是“标准”规范表示。

答案 1 :(得分:40)

我在Chris Storms博客上找到Lasse的答案是一个很好的解释。

Dart Constant Constructors

我希望他们不介意我复制内容。

  

这是对最终字段的精细解释,但事实并非如此   解释const构造函数。这些示例中没有任何内容实际使用   构造函数是const构造函数。任何课程都可以有决赛   字段,const构造函数与否。

     

Dart中的字段实际上是一个匿名存储位置   一个自动创建的getter和setter,用于读取和更新   存储,它也可以在构造函数的初始化程序中初始化   名单。

     

最后一个字段是相同的,只是没有setter,所以唯一的方法   设置它的值是在构造函数初始化列表中,并且没有   之后改变价值的方式 - 因此是“最终”。

     

const构造函数的要点是不要初始化final字段   生成构造函数可以做到这一点。重点是创造   编译时常量值:所有字段值所在的对象   已经在编译时知道,没有执行任何语句。

     

这对类和构造函数设置了一些限制。一个常数   构造函数不能有一个体(没有执行语句!)及其类   不能有任何非最终字段(我们在编译时“知道”的值   时间一定不能以后改变)。初始化列表也必须   只将字段初始化为其他编译时常量,所以   右侧仅限于“编译时常数   表达式“[1]。它必须以”const“为前缀 - 否则你   只是得到一个恰好满足那些的普通构造函数   要求。这很好,它不是常量   构造

     

为了使用const构造函数来实际创建编译时   常量对象,然后用a中的“const”替换“new”   “新” - 表达。您仍然可以将“new”与const构造函数一起使用,   它仍然会创建一个对象,但它只是一个普通的新对象   object,不是编译时常量值。那就是:一个常数   构造函数也可以用作普通构造函数来创建对象   在运行时,以及在。创建编译时常量对象   编译时间。

     

所以,举个例子:

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

编译时常量是规范化的。这意味着无论如何   很多时候你写“const Point(0,0)”,你只创建一个对象。   这可能是有用的 - 但不像看起来那么多,因为你可以   只需创建一个const变量来保存该值并使用该变量   代替。

     

那么,编译时常量到底有什么用呢?

     
      
  • 它们对枚举很有用。
  •   
  • 您可以在交换机情况下使用编译时常量值。
  •   
  • 它们用作注释。
  •   
     

编译时常数在Dart切换之前更为重要   懒惰地初始化变量。在此之前,你只能申报   一个初始化的全局变量,如“var x = foo;”如果“foo”是一个   编译时常量。没有这个要求,大多数程序都可以   不使用任何const对象编写

     

因此,简短摘要:Const构造函数仅用于创建   编译时常量值。

     

/ L

     

[1]或者说:“潜在的编译时常量表达式”   因为它也可能引用构造函数参数。   [2]所以是的,一个类可以同时拥有const和非const构造函数。

https://github.com/dart-lang/sdk/issues/36079中也讨论了这个主题并提出了一些有趣的评论。

答案 2 :(得分:12)

非常详细的解释,但对于实际上正在寻找使用const构造函数的用户

它用于提高Flutter性能,因为它有助于Flutter进行以下操作: 仅重建应更新的小部件。 在StateFulWidgets中使用setState(),将仅重建那些组件 不是const构造函数的

可以用example->

来解释
    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');
        },
      ),
    );
  }
}

在此示例中,仅应更改文本标题,因此仅应重建此窗口小部件,因此将所有其他窗口小部件用作const构造函数将有助于Flutter进行相同操作以提高性能。

答案 3 :(得分:0)

const实例真正由最终字段决定的示例演示 在这种情况下,它无法在编译时预测。

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

<击>

现在飞镖会检查它。

飞镖分析:

  

[dart]无法定义'const'构造函数,因为字段'j'用非常数值初始化

运行时错误:

  

/main.dart':错误:第5行pos 17:表达式不是有效的编译时常量     final int j = new DateTime.now()。millisecond;

答案 4 :(得分:0)

所有热门答案都用于了解有关 const 的一切以及如何使用它们。

如果您想找到所有可能的代码,使它们在您的项目中保持不变,该怎么办?

为此,您可以使用 lint 包!

基于 lint 文档:

<块引用>

lint 是一个精心挑选的、开源的、社区驱动的集合 Dart 和 Flutter 项目的 lint 规则。这套规则遵循 Effective Dart: Style Guide

答案 5 :(得分:0)

在此视频中,您将了解我们为什么需要它。 https://www.youtube.com/watch?v=B1fIqdqwWw8&t=558s09:1816:09


在文档中:https://dart.dev/guides/language/language-tour

<块引用>

常量构造函数。使用 a 创建编译时常量 常量构造函数,将 const 关键字放在构造函数之前 名称:

> var p = const ImmutablePoint(2, 2);
<块引用>

构造两个相同的 编译时常量产生一个单一的、规范的实例:

 var a = const ImmutablePoint(1, 1);
 var b = const ImmutablePoint(1,1);
   
 assert(identical(a, b)); // They are the same instance!
<块引用>

在一个 常量上下文,您可以在构造函数或 文字。例如,看看这段代码,它创建了一个常量映射:

 // Lots of const keywords here.
 const pointAndLine = const {  
   'point': const [const ImmutablePoint(0, 0)],
   'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
 };
<块引用>

你可以省略 除了第一次使用 const 关键字:

 // Only one const, which establishes the constant context.
 const pointAndLine = {   
    'point': [ImmutablePoint(0, 0)],
    'line':  [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };
<块引用>

如果一个常数 构造函数在常量上下文之外,并且在不使用的情况下调用 const,它创建一个非常量对象:

> var a = const ImmutablePoint(1, 1); // Creates a constant var b =
> ImmutablePoint(1, 1); // Does NOT create a constant
> 
> assert(!identical(a, b));// NOT the same instance!

有关更多信息,您可以查看以下 2 个答案:

1- https://stackoverflow.com/a/21746692/14409491

2- https://stackoverflow.com/a/21745617/14409491