在构造函数或声明中初始化类字段?

mmc*_*ole 394 java

我最近一直在用C#和Java编程,我很好奇最好的地方是初始化我的类字段.

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

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

    public void Roll()
    {
       // ......
    }
}
Run Code Online (Sandbox Code Playgroud)

或者在构造函数中?:

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

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

    public void Roll()
    {
        // .....
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的很好奇你们有些人认为最好的做法.我想保持一致并坚持一种方法.

kok*_*kos 294

我的规则:

  1. 使用默认值在声明中没有初始化(null,false,0,0.0...).
  2. 如果您没有更改字段值的构造函数参数,则首选声明中的初始化.
  3. 如果由于构造函数参数而改变字段的值,则将初始化放在构造函数中.
  4. 在你的实践中保持一致(最重要的规则).

  • 我不同意规则1 - 没有指定默认值(无论是否由编译器初始化)你要离开开发人员*猜*或者去寻找关于该特定语言的默认值是什么的文档.出于可读性目的,我总是指定默认值. (91认同)
  • 我不同意那些不同意规则1的人.期望别人学习C#语言是可以的.正如我们不会对每个`foreach`循环注释"这会对列表中的所有项重复以下内容",我们不需要不断重述C#的默认值.我们也不需要假装C#具有未初始化的语义.由于缺少一个值具有明确的含义,所以可以将其删除.如果明确是理想的,那么在创建新代理时应始终使用`new`(如C#1中所要求的那样).但谁这样做?该语言专为认真的编码人员而设计. (32认同)
  • 类型`default(T)`的默认值始终是内部二进制表示为"0"的值. (31认同)
  • 无论您是否喜欢规则1,它都不能与只读字段一起使用,*必须*在构造函数完成时显式初始化. (13认同)
  • 我希望kokos意味着你不应该将成员初始化为它们的默认值(0,false,null等),因为编译器会为你做(1).但是,如果要将字段初始化为除默认值以外的任何值,则应在声明(2.)中执行此操作.我认为可能是使用"默认"这个词让你感到困惑. (4认同)
  • 使用默认值进行显式初始化会向我建议在其他任何内容写入之前可能会读取该值; 因此,我认为这些声明在含义正确的情况下是有用的. (3认同)

Qui*_*ome 148

在C#中没关系.您提供的两个代码示例完全相同.在第一个例子中,C#编译器(或它是CLR?)将构造一个空构造函数并初始化变量,就像它们在构造函数中一样.如果已有构造函数,则"上方"的任何初始化将被移动到其顶部.

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

  • +1构造函数链接参数很强 (77认同)
  • 实际上它确实很重要.如果基类构造函数调用在派生类中重写的虚方法(通常是一个坏主意,但可能发生),那么使用实例变量初始化程序时,该变量将在调用方法时初始化 - 而使用初始化构造函数,他们不会.(在调用基类构造函数之前*执行实例变量初始值设定项.) (27认同)
  • 如果您选择使用GetUninitializedObject初始化类,那么这是不正确的.无论在ctor中的任何内容都不会被触及,但是现场声明将被运行. (4认同)
  • 真?我发誓我是通过C#(我认为是第二版)从Richter的CLR中获取此信息的,其要旨是这是语法糖(我可能看错了吗?),而CLR只是将变量塞入了构造函数中。但是您要说明的不是这种情况,即在疯狂的场景中调用成员初始化可以在ctor初始化之前触发,这是在基本ctor中调用虚拟并在所讨论的类中进行覆盖。我理解正确吗?您刚刚发现了吗?对于这个关于5岁帖子的最新评论感到困惑(OMG已经5年了?)。 (2认同)

Tom*_*ine 15

C#的语义与Java略有不同.在C#中,声明中的赋值是在调用超类构造函数之前执行的.在Java中,它立即完成,允许使用'this'(对匿名内部类特别有用),并且意味着两个表单的语义确实匹配.

如果可以,请将字段设为最终字段.


xji*_*xji 14

我认为有一点需要注意.我曾经犯过这样一个错误:在派生类中,我试图"初始化声明"从抽象基类继承的字段.结果是存在两组字段,一组是"base",另一组是新声明的字段,调试花了我一些时间.

教训:要初始化继承的字段,你可以在构造函数中完成.


Rae*_*ald 7

在 Java 中,带有声明的初始化器意味着该字段始终以相同的方式初始化,无论使用哪个构造函数(如果有多个)或构造函数的参数(如果它们有参数),尽管构造函数可能随后更改值(如果不是最终值)。因此,使用带有声明的初始值设定项向读者表明,初始化值是该字段在所有情况下都具有的值,无论使用哪个构造函数,也无论传递给任何构造函数的参数如何。因此,仅当且始终当所有构造对象的值相同时才使用带有声明的初始值设定项。


Noe*_*oel 6

假设您的示例中的类型,肯定更喜欢初始化构造函数中的字段.特殊情况是:

  • 静态类/方法中的字段
  • 输入的字段为static/final/et al

我总是将类顶部的字段列表视为目录(此处包含的内容,而不是如何使用),以及构造函数作为简介.方法当然是章节.

  • 为什么会“一定”如此呢?您仅提供了一种风格偏好,而没有解释其原因。哦等等,没关系,根据@quibblesome的[答案](http://stackoverflow.com/a/25130/728675),它们是“完全等效的”,所以这实际上只是个人风格偏好。 (2认同)

Mir*_*lec 5

有许多不同的情况。

我只需要一个空列表

情况很清楚。我只需要准备我的列表并防止在有人向列表中添加项目时抛出异常。

public class CsvFile
{
    private List<CsvRow> lines = new List<CsvRow>();

    public CsvFile()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道价值观

我完全知道默认情况下我想要什么值,或者我需要使用其他一些逻辑。

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = new List<string>() {"usernameA", "usernameB"};
    }
}
Run Code Online (Sandbox Code Playgroud)

或者

public class AdminTeam
{
    private List<string> usernames;

    public AdminTeam()
    {
         usernames = GetDefaultUsers(2);
    }
}
Run Code Online (Sandbox Code Playgroud)

具有可能值的空列表

有时我希望默认情况下有一个空列表,可以通过另一个构造函数添加值。

public class AdminTeam
{
    private List<string> usernames = new List<string>();

    public AdminTeam()
    {
    }

    public AdminTeam(List<string> admins)
    {
         admins.ForEach(x => usernames.Add(x));
    }
}
Run Code Online (Sandbox Code Playgroud)