【发布时间】:2018-04-09 21:55:14
【问题描述】:
根据this excellent explanation const Dart 中的表达式是“完全不可变的”,这意味着内部的任何内容都不会改变,因此整个表达式将始终表示相同的东西。这对编译器很有用,因为它可以一次生成整个对象图并在每次出现这样的表达式时重新使用它,而且程序员知道这样的表达式——即使它是深度嵌套的——仍然是有用的遵循价值语义,不会在我背后做任何事情。
我正在使用编译器的这些优化来使用结构良好的对象模型(例如,而不是将其手动编码为位向量)并且仍然可以获得良好的性能。由于我们也可以通过使用 static final 习惯用法使某些值成为运行时常量来“显式散列”某些值,从而获得其中的一些好处,因此出现的问题是,在哪种情况下使用这两种风格中的哪一种是好的?
考虑以下示例:
enum ShaftType { RING, SUN, CARRIER }
class Shaft {
final int index;
final ShaftType type;
Shaft(this.type, this.index) {
assert((type == ShaftType.CARRIER) == (index == null));
}
const Shaft.CARRIER()
: type = ShaftType.CARRIER,
index = null;
const Shaft.RING(this.index) : type = ShaftType.RING;
const Shaft.SUN(this.index) : type = ShaftType.SUN;
}
class GearPath {
final Shaft input, output, fixed;
GearPath({this.input, this.output, this.fixed}) {
// input and output must be set
assert(null != input && null != output);
// fixed shaft can't be anything else
assert(fixed != input && fixed != output);
}
GearPath.carrierToFirstRingFixedSun(int i)
: input = const Shaft.CARRIER(),
output = const Shaft.RING(0),
fixed = new Shaft.SUN(i) {}
static final singleFixedSunUp = new GearPath(
input: const Shaft.CARRIER(),
output: const Shaft.RING(0),
fixed: const Shaft.SUN(0),
);
static final directDrive = new GearPath(
input: const Shaft.CARRIER(),
output: const Shaft.CARRIER(),
fixed: null,
);
// ...
}
我不能使主要的Shaft(..) 和GearStage(..) 构造函数const 因为我想检查一些约束,但我可以提供符合的特殊情况构造函数(例如Shaft.SUN(int i)、Shaft.CARRIER())通过设计(至少部分)限制这些约束,并为用户提供这些常见值的易读简写。
另一方面,当const 构造函数没有参数时,我也可以将它写为static final 成员,就像我对GearStage.directDrive 所做的那样。如果所有用户都引用这个静态成员而不是重新创建值,我们还可以获得共享内存和快速比较(引用同一对象)的好处。我不能将这个定义的右侧声明为 const,因为它使用了非常量构造函数,但是开发人员可以从上下文中看到这确实是一个常量值,而不是隐藏在静态字段中的全局可变单例。所以实际上它应该和 const 构造函数一样好,对吧?
由于我没有发现这描述了作为最佳实践的任何地方,我的问题很简单,如果这确实是在const 构造函数和static final“命名值实例”之间进行组合和权衡的好方法?
最后,我想知道是否有办法将GearPath.carrierToFirstRingFixedSun(int i) 也声明为 const 构造函数?目前我不能,因为const Shaft.SUN(i) 抱怨i 不是恒定的。
【问题讨论】:
标签: dart