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



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

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

Const构造函数创建一个"被规范化的";例子

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

规范化:

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


这意味着像const Foo(1, 1)这样的常量表达式可以表示任何对虚拟机中的比较有用的可用形式。

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

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

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

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

p.S.

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

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

我发现Lasse在Chris Storms博客上的回答是一个很好的解释。

Dart常量构造函数

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

这是对最终字段的一个很好的解释,但实际上并不是解释const构造函数。这些示例中没有实际使用构造函数是const构造函数。任何课程都可以进行期末考试字段,const构造函数与否。

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

最后一个字段是一样的,只是没有setter,所以唯一的方法是set的值在构造函数初始值设定项列表中,并且没有之后更改值的方法-因此是"最终"。

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

这对类和构造函数设置了一些限制。常量构造函数不能有主体(没有执行任何语句!)及其类不能有任何非最终字段(我们在编译时"知道"的值时间以后一定不能改变)。初始值设定项列表还必须仅将字段初始化为其他编译时常量,因此右侧限制为"编译时间常数"expressions"[1]。并且它必须以"const"为前缀,否则只需获得一个恰好满足这些条件的普通构造函数要求。这很好,只是不算意外构造函数。

为了使用const构造函数来实际创建编译时常量对象,然后将中的"new"替换为"const"new"-表达式。您仍然可以将"new"与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! 
}

编译时间常数是规范化的。这意味着无论怎样多次写入"const Point(0,0)"时,只创建一个对象。这可能很有用,但并不像看上去那么多,因为你可以只需创建一个const变量来保存该值并使用该变量相反

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

  • 它们对枚举很有用
  • 您可以在开关情况下使用编译时常数值
  • 它们被用作注释

在Dart切换之前,编译时间常数曾经更重要延迟初始化变量。在此之前,您只能声明如果"foo"是编译时间常数。如果没有这一要求,大多数程序都可以在不使用任何常量对象的情况下编写

所以,简单总结一下:Const构造函数只是用于创建编译时间常量值。

/L

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

中也讨论了此主题https://github.com/dart-lang/sdk/issues/36079还有一些有趣的评论。

非常详细地解释了这一点,但对于那些实际上正在寻找const构造函数用法的用户来说

它用于提高Flutter性能,因为它有助于Flutter只重建应该更新的小部件。使用时的方法在StateFulWidgets中的setState(),只有那些组件将被重新生成是非常量构造函数

可以用例子来解释->

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标题应该被更改,所以只有这个小部件应该被重建,所以将所有其他小部件作为const构造函数将有助于flutter做同样的事情来提高性能。

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


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

常量构造函数。使用常量构造函数,将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

一个实例演示,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行位置17:表达式不是有效的编译时常数final int j=new DateTime.now().毫秒;

最新更新