字段初始化

时间:2009-12-17 09:53:56

标签: c# .net oop

以下两种字段初始化方式有什么区别吗?何时使用哪一个?

第一种方式

public class Class1
{
   private SomeClass someclass;

   public Class1()
   {
       someclass = new SomeClass(some arg);
   }
}

第二种方式

public class Class1
{
   private SomeClass someclass = new SomeClass(some arg);
}

第二个例子中的字段可能是只读的。

9 个答案:

答案 0 :(得分:35)

初始化内联字段时,无法使用this关键字。这样做的原因是代码的执行顺序:对于所有意图和目的,初始化字段内联的代码在类的构造函数之前运行(即C#编译器将阻止访问this关键词)。基本上这意味着这将无法编译:

public class Class1
{
   private SomeClass someclass = new SomeClass(this);

   public Class1()
   {
   }
}

但这会:

public class Class1
{
   private SomeClass someclass;

   public Class1()
   {
       someclass = new SomeClass(this);
   }
}

这是一个微妙的差异,但值得记下。

使用继承时,两个版本之间的其他差异才非常明显。如果你有两个相互继承的类,那么首先初始化派生类中的字段,然后初始化基类中的字段,然后调用基类的构造函数,最后是构造函数。将调用派生类。在某些情况下你需要非常小心,因为如果你没有意识到发生了什么,它可能导致水果沙拉的复杂性(其中一个涉及在基类构造函数中调用虚方法,但是几乎从来都不是一个明智的举动)。下面是一个例子:

class BaseClass
{
    private readonly object objectA = new object(); // Second
    private readonly object objectB;

    public BaseClass()
    {
        this.objectB = new object(); // Third
    }
}

class DerivedClass : BaseClass
{
    private object objectC = new object(); // First
    private object objectD;

    public DerivedClass()
    {
        this.objectD = new object(); // Forth
    }
}

您需要在初始化字段的所有行上设置断点,以便能够看到正确的序列。

答案 1 :(得分:34)

存在一个细微的差别,因为第二个示例中的字段将在基类中的字段初始化之前初始化,第一个示例中的字段将在之后初始化。然而,这很少有任何影响。

很大程度上,这是风格和偏好的问题。我个人更喜欢第二个,因为它使构造函数更清晰,可以进行更多基于逻辑的初始化,但是有一个很好的例子可以让所有的初始化都在构造函数中完成。

为了完整起见,初始化顺序为:

  1. 静态字段
  2. 静态构造函数
  3. 实例字段
  4. 基础静态字段
  5. 基础静态构造函数
  6. 基本实例字段
  7. 基础构造函数
  8. 构造

答案 2 :(得分:6)

除了代码行数之外,还有细微差别。

例如,字段初始化在构造函数运行之前发生。在你的例子中没有太大的区别,但要记住这一点。

我会将第二个示例中的字段初始化保留为简单的(字符串或整数),以避免在初始化期间发生可能的异常。

如上所述,在两种方式中,字段都可以只读,因为readonly字段只能在构造期间写入。

答案 3 :(得分:2)

事实上,这两个类中的字段都可以是只读的。

答案 4 :(得分:2)

存在差异。

想象一下,你有一个包含多个构造函数的类。使用第一种方法,每个构造函数都需要创建这些对象。这可能是预期的行为,因为您可能希望每次都以不同方式创建对象。 对构造函数的这个额外责任可能是一件坏事,因为如果你不记得在每个构造函数中初始化变量,那么你最终会得到一个null对象。

要考虑的效率很小,但它并不重要 - 第一种方式需要两个赋值,首先是null,然后是创建的对象,而第二种方式是对象在一步中创建和初始化。 / p>

然后考虑一下静态变量。静态变量必须以第二种方式声明,因为永远不会保证会创建类的实例。

答案 5 :(得分:1)

由于异常管理和调试商品,建议使用构造方法。

如果某个字段应该只读,则可以声明一个只读属性(仅限getter)。

类的instance field variable initializers对应于在进入该类的任何一个实例构造函数时立即执行的赋值序列。变量初始值设定项以它们出现在类声明中的文本顺序执行。

实例字段的变量初始值设定项无法引用正在创建的实例。因此,在变量初始化程序中引用它是一个编译时错误,因为变量初始化程序通过简单名称引用任何实例成员是编译时错误。

所有字段都会出现默认value initialization,包括具有可变初始值设定项的字段。因此,在初始化类时,首先将该类中的所有静态字段初始化为其默认值,然后以文本顺序执行静态字段初始值设定项。同样,当创建类的实例时,该实例中的所有实例字段首先被初始化为其默认值,然后实例字段初始化器以文本顺序执行。

答案 6 :(得分:0)

如果你的“some arg”参数不是静态的,那么第一个是有用的。如果只能通过构造函数获得该参数,那么这就是采用的方法。 第二种方式出现问题。如果在SomeClass的实例化期间抛出异常,则无法在Class1中捕获该异常。

祝你好运,
费边

答案 7 :(得分:0)

差别很小。编译器将按照定义的顺序将所有内联初始值设定项放在构造函数的开头。

如果你需要复杂的逻辑来初始化字段,你可能想要使用构造函数方法,否则我认为内联方法更清晰,更容易维护,因为编译器会为你调用它。

答案 8 :(得分:0)

实际上我更喜欢第二个用于可读性和易于调试,你可以通过try catch包装调用,但在第一个你不能。