一位同事问我这个问题,我们是否应该始终在类中包含默认构造函数?如果是这样,为什么?如果没有,为什么不呢?
例
public class Foo {
Foo() { }
Foo(int x, int y) {
...
}
}
我也想从专家那里得到一些启发。
Answers:
您必须记住,如果不提供重载的构造函数,则编译器将为您生成默认的构造函数。这意味着,如果您只是
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
默认情况下是班级成员)...
Foo f = new Foo();
。但是,将类更改为第三个示例中显示的类后,Foo f = new Foo();
将导致编译器错误-这是我的观点。只要您没有构造函数,编译器就会为您添加默认构造函数。一旦添加任何其他构造函数,编译器便不再添加默认构造函数,并且依赖于它的当前代码也会中断。
有些事情(例如序列化)需要默认的构造函数。但是,除此之外,只有在合理的情况下才应添加默认构造函数。
例如,如果Foo.X
andFoo.Y
属性在构造之后是不可变的,则默认构造函数实际上没有任何意义。即使将其用于“空” Foo,Empty
也更容易发现静态访问器。
ISerializable
,并添加一个构造函数(private
如封闭,protected
与签名以其他方式)ClassName(SerializationInfo info, StreamingContext context)
,反映了实现的ISerializable.GetObjectData
。
我会说没有,绝对不是永远。假设您有一个带有一些只读字段的类,必须将其初始化为某个值,并且没有合理的默认值(或者您不希望有这样的默认值)?在这种情况下,我认为无参数构造函数没有任何意义。
是最好有一个默认的构造函数,以确保您避免任何混乱。我见过人们只是在默认构造函数内不做任何操作(即使在Microsoft自己的类中),但仍喜欢保留它,因为对象确实会自动获得default(type)。没有指定默认构造函数的类,.NET将自动为您添加它们。
如果您使用现有的序列化程序,则序列化需要使用默认的构造函数,因为它对于通用序列化程序有意义,否则,您需要创建自己的实现。
ISerializable
,并添加一个构造函数(private
如封闭,protected
与签名以其他方式)ClassName(SerializationInfo info, StreamingContext context)
,反映了实现的ISerializable.GetObjectData
。
附带说明一下,当使用struct而不是class时,请注意,没有办法将默认构造函数排除在外,也无法自行定义,因此无论您定义的是哪个构造函数,都要确保默认状态为struct(当所有变量都设置为默认状态时(对于值类型通常为0,对于引用类型为null)不会破坏您对struct的实现。
如果泛型类型具有默认构造函数,则只能使用C#手段实例化(无反射)。另外,new()
必须指定通用类型约束:
void Construct<T>()
where T : new()
{
var t = new T();
...
}
使用没有默认构造函数的类型作为泛型类型参数调用此方法会导致编译器错误。