【问题标题】:Should we always include a default constructor in the class?我们应该总是在类中包含一个默认构造函数吗?
【发布时间】:2011-04-11 03:50:22
【问题描述】:

一位同事问我这个问题,我们应该始终在类中包含默认构造函数吗?如果是这样,为什么?如果没有,为什么不呢?

示例

public class Foo {

    Foo() { }

    Foo(int x, int y) {
        ...
    } 

}

我也有兴趣从专家那里得到一些启示。

【问题讨论】:

  • 像往常一样 - 仅在必要时。

标签: c# .net clr default-constructor


【解决方案1】:

有些事情(比如序列化)需要一个默认的构造函数。但是,除此之外,只有在有意义的情况下才应添加默认构造函数。

例如,如果Foo.XFoo.Y 属性在构造后是不可变的,那么默认构造函数就没有意义了。即使将其用于“空”Foo,静态Empty 访问器也更容易被发现。

【讨论】:

  • 序列化不需要默认构造函数。如果你有一个你想要可序列化的类,而默认构造函数没有意义,那么实现ISerializable,并添加一个构造函数(private,如果是密封的,protected,否则)带有签名ClassName(SerializationInfo info, StreamingContext context)镜像ISerializable.GetObjectData的实现。
【解决方案2】:

我会说,绝对不是总是。假设您有一个包含一些只读字段的类,这些字段必须初始化为某个值,并且没有合理的默认值(或者您不希望有)?在这种情况下,我认为无参数构造函数没有意义。

【讨论】:

  • 完全同意。类应始终以有效状态实例化。如果您有一个默认 ctor 并在构建后设置属性,那么该实例何时有效?设置一些属性后?全部设置后?谁知道呢。
【解决方案3】:

在大多数情况下,默认构造函数是个好主意。但是由于您使用“始终”一词,因此只需要一个反例即可。如果您查看框架,您会发现很多。例如,System.Web.HttpContext。

【讨论】:

    【解决方案4】:

    你要记住,如果你不提供一个重载的构造函数,编译器会为你生成一个默认的构造函数。这意味着,如果你有

    public class Foo
    { 
    } 
    

    编译器会将其生成为:

    public class Foo
    { 
        public Foo() { }  
    } 
    

    但是,只要你添加了另一个构造函数

    public class Foo
    { 
        public Foo(int x, int y)
        { 
            // ... 
        }  
    } 
    

    编译器将不再为您自动生成默认构造函数。如果该类已经在其他依赖于默认构造函数 Foo f = new Foo(); 的代码中使用,那么该代码现在会中断。

    如果您不希望有人能够在不提供数据的情况下初始化类,您应该创建一个默认构造函数 private,以明确说明您正在防止在没有输入数据的情况下构造实例。

    但是,有时需要提供默认构造函数(无论是公共的还是私有的)。如前所述,某些类型的序列化需要默认构造函数。有时,一个类有多个参数化构造函数,但也需要“较低级别”的初始化,在这种情况下,可以使用从参数化构造函数链接的私有默认构造函数。

    public class Foo
    {
       private Foo()
       {
          // do some low level initialization here
       }
    
       public Foo(int x, int y)
          : this()
       {
          // ...
       }
    
       public Foo(int x, int y, int z)
          : this()
       {
          // ...
       }
    }
    

    【讨论】:

    • 我认为你缺少一些 public 修饰符(类成员默认为 private)...
    • @Dan Tao:是的,我是。感谢@Anthony Pegram 为我添加它们。
    • @Jon Hanna:修复了类名后面的 ()。但是,您对答案错误的评论是……嗯,错误的。第一个代码将编译并允许您编写诸如Foo f = new Foo(); 之类的代码。但是,一旦您将类更改为第三个示例中显示的类,Foo f = new Foo(); 将导致编译器错误 - 这是我的观点。只要您有 no 构造函数,编译器就会为您添加默认构造函数。只要您添加 any 其他构造函数,编译器就不再添加默认构造函数和依赖它存在的代码中断。
    • +1 这个。创建空的默认构造函数很烦 IMO,像 ReSharper 这样的工具只是将其标记为死代码。
    • @Scott。是的,只是扫描太快导致误读。我的错。
    【解决方案5】:

    是的 最好有一个默认构造函数来确保避免任何混淆。我见过人们在默认构造函数中什么都不做(即使在微软自己的类中),但仍然喜欢保留它,因为对象会自动获取默认(类型)。没有指定默认构造函数的类,.NET 会自动为你添加。

    如果您使用现有的序列化程序,序列化需要默认构造函数,因为它对通用序列化程序有意义,否则您需要创建自己的实现。

    【讨论】:

    • 序列化不需要默认构造函数。如果你有一个你想要可序列化的类,而默认构造函数没有意义,那么实现ISerializable,并添加一个构造函数(private 如果密封,protected 否则)带有签名ClassName(SerializationInfo info, StreamingContext context)镜像ISerializable.GetObjectData的实现。
    • 点数。其实我知道,但总的来说人们确实想要简单。但是您为答案添加了一点。谢谢。
    【解决方案6】:

    只有在拥有这样一个对象有意义的情况下,拥有一个默认构造函数才是一个好主意。

    如果你从这样的构造函数中生成了一个不处于有效状态的对象,那么它唯一能做的就是引入一个错误。

    【讨论】:

      【解决方案7】:

      附带说明一下,当使用 struct 而不是 class 时,请注意 没有办法 将默认构造函数排除在外,也不可能自己定义它,因此无论您使用哪种构造函数定义,确保结构的默认状态(当所有变量都设置为默认状态时(通常为值类型为 0,引用类型为 null)不会破坏结构的实现。

      【讨论】:

        【解决方案8】:

        如果泛型类型具有默认构造函数,则只能使用 C# 方法(无需反射)实例化。此外,必须指定 new() 泛型类型约束:

        void Construct<T>()
            where T : new()
        {
            var t = new T();
            ...
        }
        

        使用没有默认构造函数的类型作为泛型类型参数调用此方法会导致编译器错误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多