【问题标题】:Dart assigning to variable right away or in constructor?Dart 立即分配给变量还是在构造函数中?
【发布时间】:2021-08-31 20:57:42
【问题描述】:

在 Dart 中,立即赋值与在 Java 中的构造函数中赋值有区别吗?

class Example {
    int x = 3;
}

class Example {
    int x;
    Example() {
        x = 3;
    }
}

我问是因为当我使用 Flutter 并试图将一个使用 setState 的函数分配给一个变量时,前一种方法是不可能的,但后一种方法是可能的。

【问题讨论】:

    标签: flutter dart construction


    【解决方案1】:

    在你的微不足道的情况下,没关系。

    通常,您可以通过以下几种方式初始化实例变量:

    内联(字段初始化器)

    class Example1 {
      T x = value;
    }
    

    优点:

    • 直接、简洁。
    • 成员将在所有构造函数中初始化。
    • 可用于初始化final 或不可为空的成员。
    • 在调用基类构造函数之前初始化成员,这在基类构造函数调用被派生类覆盖的成员函数时很重要。

    缺点:

    初始化列表

    class Example2 {
      T x;
    
      Example2() : x = value;
    }
    

    优点:

    • 可用于初始化final 或不可为空的成员。
    • 在调用基类构造函数之前初始化成员,这在基类构造函数调用被派生类覆盖的成员函数时很重要。
    • 可以利用构造参数。
    • 初始化变量总是引用成员变量,从不引用构造函数参数。

    缺点:

    • 如果类有多个构造函数,则需要重复初始化,或者构造函数应该重定向到一个通用构造函数。
    • 不能依赖this,因为初始化发生在this 生效之前(即不能依赖其他实例成员)。
    • 只能初始化封闭类的成员。因为初始化列表是在调用基类构造函数之前执行的,所以它们不能设置基类成员。

    构造函数体

    class Example3 {
      T x;
    
      Example3() {
        x = value;
      } 
    }
    

    优点:

    • 可以利用构造参数。
    • 可用于执行更复杂的初始化,例如无法通过单个表达式初始化成员的情况。
    • 可以使用this(即可以使用其他实例成员)。
    • 可用于设置基类成员。

    缺点:

    • 不能用于初始化final 或不可为空的成员。
    • 如果类有多个构造函数,则需要复制初始化或重构初始化代码(例如但不限于重定向到通用构造函数)。
    • 在调用基类构造函数之后初始化成员。
    • 如果构造函数有一个隐藏成员变量的参数,很容易意外引用参数而不是成员。 (详情请参阅https://github.com/dart-lang/linter/issues/2552。)

    我可能忘记了一些要点,但我认为应该涵盖主要的。

    直接,内联初始化首先发生,然后是初始化列表,然后是构造函数体。另请参阅 Difference between assigning the values in parameter list and initialiser list,它解释了为什么 this 仅对对象初始化的后期阶段有效。

    作为一个重要的成员初始化的例子:

    class Base {
      Base() {
        doSomething();
      }
    
      void doSomething() {}
    }
    
    class DerivedEarly extends Base {
      int? x;
    
      DerivedEarly() : x = 42;
    
      @override
      void doSomething() => print(x);
    }
    
    class DerivedLate extends Base {
      int? x;
    
      DerivedLate() {
        x = 42;
      }
    
      @override
      void doSomething() => print(x);
    }
    
    void main() {
      DerivedEarly(); // Prints: 42
      DerivedLate(); // Prints: null
    }
    

    【讨论】:

    • 感谢您提供此信息并使其保持最新状态,内容丰富且井井有条。
    • 如您的列表所示,初始化相互依赖的最终成员目前需要使用 late 关键字,这有点违反直觉。有一个issue on Github 讨论了这个问题。
    【解决方案2】:

    尽可能使用初始化形式使您的代码更简洁。

    class Point {
      double x, y;
      Point(this.x, this.y);
    }
    

    这个this。构造函数参数之前的语法称为“初始化形式”。你不能总是利用它。有时您希望有一个命名参数,其名称与您正在初始化的字段的名称不匹配。但是当你可以使用初始化形式时,你应该这样做。

    class Point {
      final num x;
      final num y;
      final num distanceFromOrigin;
    
      // Special syntax
      Point(this.x, this.y) :
        distanceFromOrigin = sqrt(pow(x, 2) + pow(y, 2));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-18
      • 1970-01-01
      • 2017-03-04
      • 2013-02-09
      • 1970-01-01
      相关资源
      最近更新 更多