C# 构造函数详解:默认构造函数、空参构造函数与继承构造 构造函数主要用来初始化对象它分为静态static构造函数和实例instance构造函数两种类别。大家应该都了解如何编写类的构造函数这里只说明默认构造函数的作用以及在类中保留默认构造函数的重要性。更准确地说是保留空参数构造函数的重要性。一、从一个简单的类开始我们先来写一个类 A代码如下public class A { public int Number; // 数字 public string Word; // 文本 }然后在 Test 类中实例化它public class Test { static void Main() { A a new A(); // 实例化A() 即为类 A 的默认构造函数 Console.WriteLine(Number {0}\nWord {1}, a.Number, a.Word); Console.Read(); } }输出的结果是Number 0 Word 二、默认构造函数到底做了什么在上面的示例中我们并没有在 A 类中显式写出构造函数。当我们没有提供任何构造函数时C# 会自动为类提供一个默认构造函数其格式为public A() { // 空白的方法体 }这个默认构造函数看起来似乎根本没什么作用没有接收任何输入参数方法体也是空的。那么它真的是无所事事、可有可无的吗回想一下构造函数的主要作用——初始化对象。再看输出结果int类型的Number被初始化为默认值0string类型的Word被初始化为默认值空引用输出为空白字符。这些工作正是由这个看似没起作用的默认构造函数完成的。三、自己提供构造函数后默认构造函数去哪了很快A 类的设计让我们并不满意我们加强了它的能力自己提供了构造函数public class A { public int Number; public string Word; public A(int number, string word) { Number number; // 标记 1 Word word; // 标记 2 } }Test 类也重新设计public class Test { static void Main() { A a new A(100, 一百); // 调用了新的带参数构造函数 Console.WriteLine(Number {0}\nWord {1}, a.Number, a.Word); Console.Read(); } }输出结果Number 100 Word 一百运行很顺利结果正确。当我们提供了构造函数时C# 认为我们已经意识到、并且有能力做好类的初始化工作于是它撤销了默认构造函数把控制权交到了我们手里。那么我们自己的构造函数能胜任默认构造函数的工作吗带着怀疑我们做个尝试将 A 类构造函数的方法体清空即注释掉标记 1 和标记 2 两行重新运行输出结果是Number 0 Word 这个结果和调用默认构造函数的结果是一样的说明我们自己提供的构造函数同样能担当默认构造函数的工作。四、继承场景下的构造函数调用顺序不久以后业务进一步扩展我们要新建一个类它继承自类 A我们把它称为类 Bpublic class A { public int Number; public string Word; public A(int number, string word) { Number number; Word word; } } public class B : A { public string Name; // 增加一个成员 public B(int number, string word) { Number number; Word word; } }修改 Test 类public class Test { static void Main() { B b new B(100, 一百); Console.WriteLine(Number {0}\nWord {1}, b.Number, b.Word); Console.Read(); } }运行后出错错误信息是“A”方法没有采用“0”个参数的重载。为什么会这样呢看起来我们并没有什么错误A 类提供了构造函数B 类也提供了构造函数。事实上B 类要完成构造必须先完成 A 类的构造。当我们采用public B(int number, string word)来构造 B 类时由于没有明确指明继承的构造方法C# 默认我们继承的是基类的默认构造函数等价于public B(int number, string word) : base()采用这条构造函数时C# 先调用类 A 的默认构造函数base()完成基类的构造然后调用 B 类的构造函数完成针对 B 类构造的剩余工作。尝试修改上面的相应代码段为// 在类 A 中添加一个空参构造函数 public class A { // …… public A() { Number 1; Word 空参构造; } // ……. } // 将类 B 构造函数改为继承 A 类空参构造 public B(int number, string word) : base()实际上我是多此一举了public B(……) : base()与public B(……)是一样的未显式指明构造函数的继承体时C# 就默认为base()。输出结果Number 100 Word 一百注意看我们给 A 类添加的空参构造函数我们在方法体内给Number赋值1给Word赋值空参构造。然而从输出结果看这些赋值都被覆盖了。这就说明base()在前执行而后才是public B(…..)的方法体。五、错误原因与两种解决方案回头来看我们那个错误的例子。遗憾的是我们在类 A 中自己提供了构造函数这导致 A 类的默认构造函数消失了当 B 类从 A 类继承默认构造函数时找不到入口于是导致了如上的错误。如果要避免错误有两种方法在 A 类中添加一个无参数的构造函数B 类构造函数显式继承 A 类的构造函数例如: base(number, word)。六、为什么 C# 不能自动识别并继承事情已经解释得很清楚了不过也许还有个小问题在 B 类中我们提供了一个和 A 类构造函数结构相同的构造函数为什么 C# 不能自动识别、自动继承呢道理很简单C# 不能靠猜测来判断我们提供的参数究竟是要用作初始化基类还是用于初始化继承类。还是来看一个例子public class A { public int Number; public string Word; public A(int number, string word) { Number number; Word word; } } public class B : A { public string Name; public B(int number, string word, string name) : base(number, word) { Number number; Word word; Name name; } } public class Test { static void Main() { B b new B(100, 一百, 初始化B的参数); Console.WriteLine(Number {0}\nWord {1}\nName {2}, b.Number, b.Word, b.Name); Console.Read(); } }输出结果是Number 100 Word 一百 Name 初始化B的参数注意在 B 类的构造函数中我们提供了三个参数其中前两个用于初始化 A 类中的成员第三个用于初始化 B 类的成员。虽然父类构造函数与子类构造函数的参数数量不一样但由于我们明确指定了继承关系它们很好地完成了工作。七、小结未显式定义构造函数时C# 会自动提供一个无参的默认构造函数负责把字段初始化为类型默认值。一旦显式定义了任何构造函数编译器就不再自动生成默认构造函数。子类构造时会先调用基类构造函数未显式指定时默认调用base()因此基类必须存在可访问的无参构造函数。若基类只提供了带参构造函数子类需通过: base(参数)显式指定要调用的基类构造函数。