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

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

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

72721 次浏览

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, 2), "hello"); // $Baz$Foo$int$1$int$2$String$hello

不是每次都重新创建常量。它们在编译时被规范化,并存储在特殊的查找表中(在那里通过它们的规范签名进行散列),之后将从这些查找表中重用它们。

注:

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

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

我在Chris Storms的博客上找到了Lasse的答案。

Dart常量构造函数 .

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

这是对final字段的一个很好的解释,但它并不是真的 解释const构造函数。这些例子中都没有实际使用 构造函数是常量构造函数。任何类都可以有final 字段,const构造函数。< / p > Dart中的字段实际上是一个匿名存储位置 属性的自动创建的getter和setter 存储,也可以在构造函数的初始化式中初始化 列表。< / p > 一个final字段是相同的,只是没有setter,所以唯一的方法是 设置它的值在构造函数初始化列表中,并且没有 在此之后更改值的方法-因此是“final” const构造函数的目的不是初始化final字段any 生成构造函数可以做到这一点。关键在于创造 编译时常量值:所有字段值所在的对象

.编译时已知,无需执行任何语句 这对类和构造函数施加了一些限制。一个常量 构造函数不能有主体(没有执行语句!)和它的类 必须没有任何非final字段(我们在编译时“知道”的值 以后的时间一定不能改变)。初始化器列表也必须如此 仅将字段初始化为其他编译时常量,因此 右边仅限于“编译时间常数” 表达式”[1]。并且它必须以“const”作为前缀,否则你 只要得到一个恰好满足这些条件的普通构造函数 要求。这很好,只是它不是const 构造函数。< / p >

为了使用const构造函数实际创建编译时 常量对象,然后将“new”替换为“const” “新”的表情。你仍然可以在常量构造函数中使用"new", 它仍然会创建一个对象,但它只是一个普通的new 对象,而不是编译时常量值。即:const 构造函数也可以用作创建对象的普通构造函数 的编译时常量对象 编译时间。< / p >

举个例子:

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变量来保存值并使用该变量 相反。< / p >

那么,编译时常数到底有什么好处呢?

  • 它们对于枚举很有用。
  • 您可以在开关情况下使用编译时常量值。
  • 它们被用作注释。
在Dart转换之前,编译时常数更重要 到延迟初始化变量。在此之前,你只能申报 一个初始化的全局变量,如"var x = foo;"如果"foo"是 编译时常量。如果没有这个要求,大多数程序都可以 没有使用任何const对象

所以,简短的总结:Const构造函数只是用于创建

.编译时间常量值

/ L

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

这个主题也在https://github.com/dart-lang/sdk/issues/36079中讨论过,并给出了一些有趣的注释。

一个const实例由final字段决定的例子 在这种情况下,它不能在编译时预测
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'被初始化为一个非常量值

运行时错误:

< p > /主要。第5行pos 17:表达式不是一个有效的编译时常量 final int j = new DateTime.now().millisecond;

.毫秒

非常详细地解释了,但适用于实际寻找const构造函数用法的用户

用于增加颤振性能,因为它有助于颤振 只重新构建应该更新的小部件。使用时的方式 在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');
},
),
);
}
}

在这个例子中,只有Text title应该被改变,所以只有这个小部件应该被重建,所以将所有其他小部件作为const构造函数将帮助flutter做同样的事情以提高性能。

在这个视频中,你会知道我们为什么需要它。https://www.youtube.com/watch?v=B1fIqdqwWw8&t=558s09:1816:09


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

< p >恒定的构造函数。类创建编译时常数 常量构造函数,将const关键字放在构造函数之前 名称:< / p >
> 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!
< p >内 常量上下文,可以省略构造函数前的const或 文字。例如,看看这段代码,它创建了一个const映射:

 // 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)], };

如果是常数 构造函数在常量上下文之外,并且在

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

要了解更多信息,您可以查看以下两个答案:

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

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