Const构造函数与静态最终规范值

时间:2017-10-28 12:02:43

标签: dart

根据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,因为我想检查一些约束,但我可以提供特殊情况构造函数(例如{{1} },Shaft.SUN(int i))通过设计符合这些约束(至少部分),并为这些常见值提供用户易读的缩写。

另一方面,当Shaft.CARRIER()构造函数没有参数时,我也可以像const那样将它写成static final成员。如果所有用户都引用此静态成员而不是再次重新创建该值,我们还可以获得共享内存和快速比较(引用同一对象)的好处。我不能将此定义的右侧声明为const,因为它使用非const构造函数,但开发人员可以从上下文中看到这确实是一个常量值,而不是隐藏在静态字段中的全局可变单例。因此,出于实际目的,它应该与const构造函数一样好,对吧?

由于我没有发现这里描述的任何地方作为最佳实践我的问题仅仅是这是否真的是在GearStage.directDrive构造函数和const之间进行组合和权衡的好方法“命名值实例“?

最后,我想知道是否有办法将static final声明为const构造函数?目前我不能,因为GearPath.carrierToFirstRingFixedSun(int i)抱怨const Shaft.SUN(i)不能保持不变。

full code of example

1 个答案:

答案 0 :(得分:2)

Dart 2将允许您在const构造函数中拥有断言(只要您的条件可以作为const表达式计算)。 然后你就可以写:

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(!identical(fixed, input) && !identical(fixed, output));

在那之前,你不能同时拥有验证和const表达式。

你仍然无法使GearPath.carrierToFirstRingFixedSun(int i) const,因为它i不是常数。 const Shaft.SUN(i)仍然不是有效的const表达式,即使i是const构造函数的参数也是如此。每个const Constructor(...)调用仍然必须创建一个对象,即使它出现在另一个const构造函数的初始化列表中。

作为一个经验法则,您应该考虑将值暴露为const而不是final是您要提交的内容。当你创建变量const时,这意味着其他人可以在另一个const表达式中使用该值,然后将变量更改为final将是一个重大变化。说某事是const是一种你应该刻意选择的承诺,而不仅仅是因为你可以。因此,请考虑变量的用例。如果你没有看到它被用在其他const表达式中,那么就把它作为最终的。