【问题标题】:Const constructor vs. static final canonical valuesconst 构造函数与静态最终规范值
【发布时间】: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 不是恒定的。

(full code of example)

【问题讨论】:

    标签: dart


    【解决方案1】:

    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) 设为常量,因为i 不是常量。 const Shaft.SUN(i) 仍然不是有效的 const 表达式,即使 i 是 const 构造函数的参数。每个const Constructor(...) 调用仍然必须只创建一个对象,即使它出现在另一个 const 构造函数的初始化列表中。

    根据经验,您应该考虑将值公开为 const 而不是 final 是否是您想要承诺的事情。当您将变量设为 const 时,意味着其他人可以在另一个 const 表达式中使用该值,然后将变量更改为 final 将是一个破坏性的更改。说某事是const 是您应该慎重选择的承诺,而不仅仅是因为您可以。因此,请考虑变量的用例。如果您没有看到它在其他 const 表达式中使用,则将其设为 final。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-19
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 2013-01-03
      相关资源
      最近更新 更多