首页 文章

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

提问于
浏览
353

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

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

14 回答

  • 3

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

  • 3

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

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

  • 4

    我的规则:

    • 不要使用声明中的默认值进行初始化( nullfalse00.0 ...) .

    • 如果没有更改字段值的构造函数参数,则首选声明中的初始化 .

    • 如果由于构造函数参数而改变字段的值,则将初始化放在构造函数中 .

    • 在练习中保持一致(最重要的规则) .

  • 137

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

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

  • 0

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

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

  • 2

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

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

  • 13

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

    • 静态类/方法中的字段

    • 字段输入为static / final / et al

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

  • 261

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

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

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

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

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

  • 6

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

  • 0

    有很多种情况 .

    I just need an empty list

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

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

    I know the values

    我确切地知道默认情况下我想拥有什么值,或者我需要使用其他逻辑 .

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

    Empty list with possible values

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

    public class AdminTeam
    {
        private List<string> usernames = new List<string>();
    
        public AdminTeam()
        {
        }
    
        public AdminTeam(List<string> admins)
        {
             admins.ForEach(x => usernames.Add(x));
        }
    }
    
  • 1

    C#的设计表明内联初始化是首选,或者它不在语言中 . 任何时候你可以避免代码中不同位置之间的交叉引用,你通常会更好 .

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

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

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

  • 15

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

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

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

    正如许多其他人所说,这取决于你的用法 . 保持简单,不要做任何你不需要的额外的事情 .

  • 2

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

  • 2

    不是关于 the best practice 的问题的直接答案,但一个重要且相关的复习点是,在泛型类定义的情况下,要么将它留在编译器上以使用默认值初始化,要么我们必须使用特殊方法将字段初始化为它们的默认值(如果这对于代码可读性是绝对必要的) .

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

相关问题