在构造函数或声明中初始化类字段?
我最近在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() { // ..... } }
我真的很好奇你们有些退伍军人认为是最好的做法。 我想要一致并坚持一种方法。
我的规则:
- 不要用声明中的默认值(
null
,false
,0.0
…)进行初始化。 - 如果您没有改变字段值的构造函数参数,则优先select声明中的初始化。
- 如果字段的值由于构造函数参数而改变,则将初始化放在构造函数中。
- 在你的实践中保持一致(最重要的规则)。
在C#中并不重要。 你给的两个代码示例是完全等价的。 在第一个例子中,C#编译器(或者它是CLR?)将构造一个空的构造函数,并像variables在构造函数中一样初始化variables。 如果已经有一个构造函数,那么在“above”之上的任何初始化将被移动到它的顶部。
就最佳实践而言,前者比后者更不容易出错,因为有人可以轻易地添加另一个构造函数,而忘记将其链接起来。
C#的语义与Java稍有不同。 在C#中声明的赋值是在调用超类的构造函数之前执行的。 在Java中,它会立即完成,允许使用“this”(对于匿名内部类特别有用),并且意味着两个表单的语义确实匹配。
如果可以的话,把田地做最后的决定。
我认为有一个警告。 我曾经犯过这样一个错误:在派生类的内部,我试图“在声明时初始化”从抽象基类inheritance的字段。 结果是存在两套领域,一个是“基础”,另一个是新申报的领域,debugging花了我相当长的时间。
经验教训:要初始化inheritance的字段,你可以在构造函数中完成。
假设在你的例子中的types,绝对喜欢在构造函数中初始化字段。 特殊情况是:
- 静态类/方法中的字段
- input为静态/最终/等的字段
我总是将列表顶部的字段列表视为目录(此处包含的内容,而不是如何使用)以及构造函数作为介绍。 方法当然是章节。
如果我告诉你,这取决于什么?
一般来说,我会初始化一切,并以一致的方式进行。 是的,它是非常明确的,但它也更容易维护。
如果我们担心performance的话,那么我只会初始化那些必须做的事情,然后把它放在最让人高兴的地方。
在一个实时系统中,我怀疑我是否甚至需要variables或常量。
而在C ++中,我经常在任何一个地方都不做任何初始化,并把它移到一个Init()函数中。 为什么? 那么,在C ++中,如果你正在初始化一些可以在对象构造过程中抛出exception的东西,那么你就会打开自己的内存泄漏。
在Java中,具有声明的初始化器意味着无论使用哪个构造函数(如果有多个构造函数)或构造函数的参数(如果它们有参数),始终以相同的方式初始化该字段,尽pipe构造函数可能随后更改值(如果不是最终的)。 因此,对一个声明使用一个初始化方法会向读者build议:初始化值是该字段在所有情况下的值 ,而不pipe使用哪个构造函数,也不pipe传递给任何构造函数的参数如何。 因此,只有在所有构造对象的值相同且总是相同的情况下,才使用声明的初始化方法。
有很多种不同的情况。
我只需要一个空的列表
情况很清楚。 我只需要准备我的列表,并防止有人添加一个项目列表时引发exception。
public class CsvFile { private List<CsvRow> lines = new List<CsvRow>(); public CsvFile() { } }
我知道价值
我完全知道我想默认的值是什么,或者我需要使用其他逻辑。
public class AdminTeam { private List<string> usernames; public AdminTeam() { usernames = new List<string>() {"usernameA", "usernameB"}; } }
要么
public class AdminTeam { private List<string> usernames; public AdminTeam() { usernames = GetDefaultUsers(2); } }
清空可能的值
有时我希望默认情况下有一个空的列表,通过另一个构造函数添加值的可能性。
public class AdminTeam { private List<string> usernames = new List<string>(); public AdminTeam() { } public AdminTeam(List<string> admins) { admins.ForEach(x => usernames.Add(x)); } }
在声明中设置值有一点性能好处。 如果你在构造函数中设置它,它实际上被设置了两次(首先是默认值,然后在ctor中重置)。
C#的devise表明,内联初始化是首选,或者它不会在语言中。 任何时候你都可以避免在代码中的不同位置之间进行交叉引用,通常情况下会更好。
还有与静态字段初始化一致的问题,为了获得最佳性能,需要内联。 框架devise指南build设者devise说:
✓考虑初始化内联静态字段而不是显式使用静态构造函数,因为运行时能够优化没有明确定义的静态构造函数的types的性能。
在这种情况下,“考虑”意味着这样做,除非有充分理由不这样做。 在静态初始化字段的情况下,如果初始化过于复杂而无法在内部编码,那么这是一个很好的理由。
保持一致是很重要的,但这是一个问自己的问题:“我有其他的构造函数吗?
通常情况下,我正在创build数据传输模型,除了工作variables外,类本身什么都不做。
在这些情况下,我通常没有任何方法或构造函数。 我觉得创build一个构造函数是为了初始化我的列表,特别是因为我可以将它们初始化为声明。
正如许多人所说,这取决于你的使用。 保持简单,不要做任何额外的事情,你不必做。
我通常尝试构造函数,除了获取依赖关系和初始化相关的实例成员。 如果你想unit testing你的课程,这将使你的生活更轻松。
如果要分配给实例variables的值不受任何将要传递给构造函数的参数的影响,则在声明时分配它。
考虑一下你有多个构造函数的情况。 对于不同的构造函数,初始化会不同吗? 如果他们将是相同的,那么为什么重复每个构造函数? 这符合kokos语句,但可能与参数无关。 比方说,例如,你想保留一个标志,显示对象是如何创build的。 那么不pipe构造函数的参数如何,这个标志对于不同的构造函数都会被初始化。 另一方面,如果对每个构造函数重复相同的初始化,则可能会(在无意中)更改某些构造函数中的初始化参数,而不会在其他构造函数中更改初始化参数。 所以,这里的基本概念是共同的代码应该有一个共同的位置,而不是在不同的位置重复。 所以我会说永远把它放在声明中,直到你有一个特定的情况下,不再适用于你。