我注意到在Dart中可以创建一个const构造函数。在文档中,它说const字是用来表示编译时间常数的。
我想知道当我使用const构造函数创建对象时会发生什么。这是否像一个在编译时总是相同且可用的不可变对象?const构造函数的概念实际上是如何工作的?const构造函数与常规构造函数有何不同?
我注意到在Dart中可以创建一个const构造函数。在文档中,它说const字是用来表示编译时间常数的。
我想知道当我使用const构造函数创建对象时会发生什么。这是否像一个在编译时总是相同且可用的不可变对象?const构造函数的概念实际上是如何工作的?const构造函数与常规构造函数有何不同?
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
不是每次都重新创建常量。它们在编译时被规范化,并存储在特殊的查找表中(在那里通过它们的规范签名进行散列),之后将从这些查找表中重用它们。
P.S.
在这些示例中使用的#Foo#int#1#int#1形式仅用于比较目的,它不是Dart VM中规范化(表示)的真正形式;
但是真正的规范化形式必须是“标准的”规范表示。
我在Chris Storms的博客上找到了Lasse的答案。
省道常数构造函数
我希望他们不介意我复制内容。
This is a fine explanation of final fields, but it doesn't really explain const constructors. Nothing in these examples actually use that the constructors are const constructors. Any class can have final fields, const constructors or not. A field in Dart is really an anonymous storage location combined with an automatically created getter and setter that reads and updates the storage, and it can also be initialized in a constructor's initializer list. A final field is the same, just without the setter, so the only way to set its value is in the constructor initializer list, and there is no way to change the value after that - hence the "final". The point of const constructors is not to initialize final fields, any generative constructor can do that. The point is to create compile-time constant values: Objects where the all field values are known already at compile time, without executing any statements. That puts some restrictions on the class and constructor. A const constructor can't have a body (no statements executed!) and its class must not have any non-final fields (the value we "know" at compile time must not be able to change later). The initializer list must also only initialize fields to other compile-time constants, so the right-hand sides are limited to "compile-time constant expressions"[1]. And it must be prefixed with "const" - otherwise you just get a normal constructor that happens to satisfy those requirements. That is perfectly fine, it's just not a const constructor. In order to use a const constructor to actually create a compile-time constant object, you then replace "new" with "const" in a "new"-expression. You can still use "new" with a const-constructor, and it will still create an object, but it will just be a normal new object, not a compile-time constant value. That is: A const constructor can also be used as a normal constructor to create objects at runtime, as well as creating compile-time constant objects at compilation time. So, as an example: 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! } Compile-time constants are canonicalized. That means the no matter how many times you write "const Point(0,0)", you only create one object. That may be useful - but not as much as it would seem, since you can just make a const variable to hold the value and use the variable instead. So, what are compile-time constants good for anyway? They are useful for enums. You can use compile-time constant values in switch cases. They are used as annotations. Compile-time constants used to be more important before Dart switched to lazily initializing variables. Before that, you could only declare an initialized global variable like "var x = foo;" if "foo" was a compile-time constant. Without that requirement, most programs can be written without using any const objects So, short summary: Const constructors are just for creating compile-time constant values. /L [1] Or really: "Potentially compile-time constant expressions" because it may also refer to the constructor parameters. [2] So yes, a class can have both const and non-const constructors at the same time.
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'被初始化为一个非常量值
运行时错误:
/主要。第5行pos 17:表达式不是一个有效的编译时常量 final int j = new DateTime.now().毫秒;
非常详细地解释了,但适用于实际寻找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=558s 09:18 - 16: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!
在一个 常量上下文,可以省略构造函数前的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)], };
如果一个常数 构造函数在常量上下文之外,并且在 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!
要了解更多信息,您可以查看以下两个答案:
1- https://stackoverflow.com/a/21746692/14409491
2- https://stackoverflow.com/a/21745617/14409491