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

mar*_*vic 77 dart

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

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

mez*_*oni 57

Const构造函数创建一个"规范化"实例.

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

规范化:

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


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

VM只需要按照它们在此const表达式中出现的顺序考虑值类型和参数.而且,当然,它们是为了优化而减少的.

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

var foo1 = const Foo(1, 1); // #Foo#int#1#int#1
var foo2 = const Foo(1, 1); // #Foo#int#1#int#1
Run Code Online (Sandbox Code Playgroud)

具有不同规范化值的常量(因为签名不同):

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
Run Code Online (Sandbox Code Playgroud)

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

PS

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

但真正的规范化形式必须是"标准的"规范表征.


Gün*_*uer 56

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

Dart Constant Constructors

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

这是对final字段的精细解释,但它并没有真正解释const构造函数.这些示例中的任何内容实际上都没有使用构造函数是const构造函数.任何类都可以有最终字段,const构造函数.

Dart中的字段实际上是一个匿名存储位置,它与自动创建的读取和更新存储的getter和setter相结合,也可以在构造函数的初始化列表中初始化.

最后一个字段是相同的,只是没有setter,因此设置其值的唯一方法是在构造函数初始化列表中,并且之后无法更改值 - 因此是"final".

const构造函数的要点不是初始化final字段,任何生成构造函数都可以这样做.关键是要创建编译时常量值:在编译时已知所有字段值的对象,而不执行任何语句.

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

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

所以,作为一个例子:

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! 
}
Run Code Online (Sandbox Code Playgroud)

编译时常量是规范化的.这意味着无论你写多少次"const Point(0,0)",你只创建一个对象.这可能很有用 - 但不像它看起来那么多,因为你只需要创建一个const变量来保存值并使用变量.

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

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

在Dart切换到懒惰初始化变量之前,编译时常量更为重要.在此之前,您只能声明一个初始化的全局变量,如"var x = foo;" 如果"foo"是编译时常量.没有这个要求,大多数程序可以在不使用任何const对象的情况下编写

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

/ L

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

  • 它们对方法签名中的默认值也很有用. (2认同)
  • 谁能向我解释这条线是如何工作的?`Point.clone(Point other): x = other.x, y = other.y;` (2认同)
  • 根据https://medium.com/@mehmetf_71205/inheriting-widgets-b7ac56dbbeb1,对于使用Flutter的小部件而言,const是一个不错的性能胜利。“使用const来构建小部件,如果没有const,就不会选择性地重建子树。 Flutter在子树中为每个小部件创建了一个新实例,并调用build()浪费了宝贵的周期,尤其是当您的构建方法繁重时。” (2认同)

B.s*_*uti 20

详细解释得很好,但对于实际正在寻找 const 构造函数用法的用户

它用于提高 Flutter 性能,因为它帮助 Flutter 仅重建应该更新的小部件。意味着在 StateFulWidgets 中使用 setState() 时,只会重建那些非 const 构造函数的组件

可以用例子来解释->

    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');
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,只有文本标题应该被改变,所以只有这个小部件应该被重建,所以将所有其他小部件设置为 const 构造函数将有助于 flutter 做同样的事情以提高性能。

  • 那么为什么不自动使用它呢?无需使用 const 关键字,编译器就会为我们完成此操作。 (12认同)