我们是否应该始终在类中包含默认构造函数?


76

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

public class Foo {

    Foo() { }

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

}

我也想从专家那里得到一些启发。


20
与往常一样-仅在必要时使用。
Arnis Lapsa'9

Answers:


121

您必须记住,如果不提供重载的构造函数,则编译器将为您生成默认的构造函数。这意味着,如果您只是

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 2010年

@丹涛:是的,我是。感谢@Anthony Pegram为我添加它们。
Scott Dorman 2010年

1
@Jon Hanna:修复了类名后的()。但是,您对答案错误的评论是……嗯,是错误的。第一个代码将编译并允许您编写诸如的代码Foo f = new Foo();。但是,将类更改为第三个示例中显示的类后,Foo f = new Foo();将导致编译器错误-这是我的观点。只要您没有构造函数,编译器就会为您添加默认构造函数。一旦添加任何其他构造函数,编译器便不再添加默认构造函数,并且依赖于它的当前代码也会中断。
Scott Dorman

2
+1。创建空的默认构造函数会让IMO烦恼,而ReSharper之类的工具只是将其标记为无效代码。
womp 2010年

1
@斯科特。是的,只是由于扫描速度过快而造成的误读。我的错。
乔恩·汉娜

19

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

例如,如果Foo.XandFoo.Y属性在构造之后是不可变的,则默认构造函数实际上没有任何意义。即使将其用于“空” Foo,Empty也更容易发现静态访问器。


3
序列化不需要默认的构造函数。如果你有你想成为serialisable,对于这一个默认的构造没有意义的一类,然后实现ISerializable,并添加一个构造函数(private如封闭,protected与签名以其他方式)ClassName(SerializationInfo info, StreamingContext context),反映了实现的ISerializable.GetObjectData
乔恩·汉纳

12

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


4
完全同意。类应始终在有效状态下实例化。如果您具有默认的ctor并在构造后设置属性,则该实例何时有效?设置一些属性后?设置全部之后?谁知道。
Cumbayah 2010年

6

如果拥有这样的对象有意义,那么使用默认构造函数只是一个好主意。

如果从这样的构造函数生成的对象处于无效状态,则它唯一能做的就是引入错误。


2

是最好有一个默认的构造函数,以确保您避免任何混乱。我见过人们只是在默认构造函数内不做任何操作(即使在Microsoft自己的类中),但仍喜欢保留它,因为对象确实会自动获得default(type)。没有指定默认构造函数的类,.NET将自动为您添加它们。

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


1
序列化不需要默认的构造函数。如果你有你想成为serialisable,对于这一个默认的构造没有意义的一类,然后实现ISerializable,并添加一个构造函数(private如封闭,protected与签名以其他方式)ClassName(SerializationInfo info, StreamingContext context),反映了实现的ISerializable.GetObjectData
乔恩·汉纳

点数了。其实我知道,但是总的来说,人们确实想简单。但您在答案中加了一点。谢谢。
2013年

1

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


0

在大多数情况下,默认构造函数是一个好主意。但是由于使用了“ always”一词,因此仅需一个反例。如果您看一下Framework,就会发现很多东西。例如,System.Web.HttpContext。


0

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.