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

时间:2008-08-23 19:59:42

标签: c# java

我最近一直在用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()
    {
        // .....
    }
}

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

15 个答案:

答案 0 :(得分:278)

我的规则:

  1. 请勿使用声明中的默认值进行初始化(nullfalse00.0 ...)。
  2. 如果您没有更改字段值的构造函数参数,则首选声明中的初始化。
  3. 如果字段的值因构造函数参数而更改,则将初始化放在构造函数中。
  4. 在练习中保持一致(最重要的规则)。

答案 1 :(得分:145)

在C#中没关系。您提供的两个代码示例完全相同。在第一个例子中,C#编译器(或它是CLR?)将构造一个空构造函数并初始化变量,就好像它们在构造函数中一样(Jon Skeet在下面的注释中解释了这一点)。 如果已有构造函数,则“上方”的任何初始化都将移到其顶部。

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

答案 2 :(得分:15)

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

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

答案 3 :(得分:14)

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

课程:要初始化继承的字段,您可以在构造函数中执行此操作。

答案 4 :(得分:6)

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

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

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

答案 5 :(得分:4)

如果我告诉你,这取决于什么?

我通常会初始化所有内容并以一致的方式执行。是的,它过于明确,但它也更容易维护。

如果我们担心性能问题,那么我只会初步完成必须完成的工作,并将其置于最有利于降压的领域。

在实时系统中,我怀疑是否甚至需要变量或常量。

在C ++中,我经常在两个地方都没有初始化,并将其移动到Init()函数中。为什么?好吧,在C ++中,如果你正在初始化一些可以在对象构造期间抛出异常的东西,你就会打开内存泄漏。

答案 6 :(得分:3)

有很多种情况。

我只需要一个空列表

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

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));
    }
}

答案 7 :(得分:3)

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

答案 8 :(得分:2)

C#的设计建议首选内联初始化,否则它不会使用该语言。任何时候您都可以避免代码中不同位置之间的交叉引用,通常情况下会更好。

还有与静态字段初始化一致的问题,需要内联以获得最佳性能。 Constructor Design的框架设计指南说明了这一点:

  

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

&#34;考虑&#34;在这种情况下意味着这样做,除非有充分的理由不这样做。在静态初始化字段的情况下,一个很好的理由是初始化太复杂而不能内联编码。

答案 9 :(得分:2)

保持一致很重要,但这是问自己的问题: &#34;我还有其他任何构造函数吗?&#34;

通常,我正在为数据传输创建模型,除了作为变量的外壳之外,类本身什么都不做。

在这些情况下,我通常没有任何方法或构造函数。创建一个专门用于初始化列表的构造函数对我来说会很愚蠢,特别是因为我可以在声明中对它们进行初始化。

正如许多其他人所说,这取决于您的使用情况。保持简单,不要做任何你不必做的额外的事情。

答案 10 :(得分:2)

考虑一下你有多个构造函数的情况。对于不同的构造函数,初始化是否会有所不同?如果它们是相同的,那么为什么要重复每个构造函数呢?这与kokos语句一致,但可能与参数无关。例如,假设您要保留一个标志来显示对象的创建方式。然后,不管构造函数参数如何,对于不同的构造函数,该标志将被不同地初始化。另一方面,如果为每个构造函数重复相同的初始化,则可能会(无意中)更改某些构造函数中的初始化参数,但不会更改其他构造函数中的初始化参数。因此,这里的基本概念是公共代码应该具有共同的位置,而不是可能在不同的位置重复。所以我会说它总是把它放在声明中,直到你遇到一个不再适合你的特定情况。

答案 11 :(得分:1)

在声明中设置值有一点性能上的好处。如果你在构造函数中设置它实际上被设置两次(首先是默认值,然后在ctor中重置)。

答案 12 :(得分:0)

我通常尝试构造函数除了获取依赖项并使用它们初始化相关实例成员之外什么也不做。如果您想对课程进行单元测试,这将使您的生活更轻松。

如果您要分配给实例变量的值不受您要传递给构造函数的任何参数的影响,则在声明时分配它。

答案 13 :(得分:0)

不是您关于最佳做法的问题的直接答案,但重要且相关的更新要点是,对于通用类定义,可以将其留给编译器以使用默认值进行初始化,或者我们必须使用一种特殊的方法将字段初始化为其默认值(如果这对于代码可读性来说是绝对必要的)。

#include <string>
#include <sstream>
#include <iostream>
#include <fstream>

using namespace std;

int main() {
    string texteins;
    stringstream a, b;
    texteins = "just some test";
    a << texteins;
    b << a.rdbuf();
    // a = "just some test"
    // b = "just some test"

    a << "more words";
    // a = "just some testmore words"
    // b = "just some test"


    stringstream c;
    fstream inFile("justsomeexample.txt", ios::in); //justsomeexample.txt = "aline\nanother line\nsome words in a line"
    c << inFile.rdbuf();
    // inFile = "";     //Why is this empty, but stringstream above not?
    // c =  "aline\nanother line\nsome words in a line"


    return 0;
}

将通用字段初始化为其默认值的特殊方法如下:

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
    }
}

答案 14 :(得分:0)

不需要逻辑或错误处理时:

  • 在声明时初始化类字段

当您需要一些逻辑或错误处理时:

  • 初始化构造函数中的类字段

当初始化值可用并且 初始化可以放在一行上。但是,这种形式的 初始化由于其简单性而受到限制。如果 初始化需要一些逻辑(例如,错误处理或 for循环以填充复杂数组),简单分配是不够的。 实例变量可以在构造函数中初始化,其中错误 处理或其他逻辑都可以使用。

来自https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html