我最近一直在用c#和Java编程,我很好奇初始化我的类字段的最佳位置在哪里。

我应该在申报时申报吗?:

public class Dice
{
    private int topFace = 1;
    private Random myRand = new Random();

    public void Roll()
    {
       // ......
    }
}

或者在构造函数中?:

public class Dice
{
    private int topFace;
    private Random myRand;

    public Dice()
    {
        topFace = 1;
        myRand = new Random();
    }

    public void Roll()
    {
        // .....
    }
}

我很好奇你们这些老兵认为最好的做法是什么。我想保持一致,坚持一种方法。


当前回答

这不是对你关于最佳实践的问题的直接回答,但一个重要的和相关的复习点是,在泛型类定义的情况下,要么让编译器使用默认值初始化它,要么我们必须使用一个特殊的方法将字段初始化为它们的默认值(如果这对代码可读性绝对必要的话)。

class MyGeneric<T>
{
    T data;
    //T data = ""; // <-- ERROR
    //T data = 0; // <-- ERROR
    //T data = null; // <-- ERROR        

    public MyGeneric()
    {
        // All of the above errors would be errors here in constructor as well
    }
}

将泛型字段初始化为默认值的特殊方法如下:

class MyGeneric<T>
{
    T data = default(T);

    public MyGeneric()
    {           
        // The same method can be used here in constructor
    }
}

其他回答

我认为有一个警告。我曾经犯过这样一个错误:在派生类内部,我试图“初始化at声明”从抽象基类继承的字段。结果是存在两组字段,一组是“base”字段,另一组是新声明的字段,这花费了我相当多的时间来调试。

教训:要初始化继承的字段,需要在构造函数内部进行。

在声明中设置值会略微提高性能。如果你在构造函数中设置它,它实际上被设置了两次(第一次为默认值,然后在ctor中重置)。

我的规则:

不要初始化声明中的默认值(null, false, 0, 0.0…) 如果没有改变字段值的构造函数形参,最好在声明中初始化。 如果字段的值因构造函数形参而改变,则将初始化放在构造函数中。 坚持练习(这是最重要的规则)。

c#的设计表明,内联初始化是首选,否则就不会出现在语言中。只要可以避免代码中不同位置之间的交叉引用,通常情况下就会更好。

还有与静态字段初始化的一致性问题,需要内联以获得最佳性能。构造函数设计框架设计指南是这样说的:

考虑内联初始化静态字段,而不是显式使用静态构造函数,因为运行时能够优化没有显式定义静态构造函数的类型的性能。

“考虑”在这里的意思是除非有很好的理由不这样做。对于静态初始化字段,一个很好的理由是初始化太复杂,不能内联编码。

在c#中,这并不重要。您给出的两个代码示例完全相同。在第一个例子中,c#编译器(或者是CLR?)将构造一个空构造函数并初始化变量,就像它们在构造函数中一样(Jon Skeet在下面的评论中解释了其中的细微差别)。 如果已经有一个构造函数,那么任何“上面”的初始化都将被移动到它的顶部。

就最佳实践而言,前者比后者更不容易出错,因为有人可能很容易添加另一个构造函数而忘记链接它。