构造函数主要用来初始化对象。它又分为静态(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被初始化为默认值0,string类型的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,我们把它称为类B:
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)
{
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类也提供了构造函数。你满腹委屈,含泪望着比尔盖茨。老比怒目圆睁,啪的给你一巴掌:“A类还没构造好,你让我怎么给你构造B类!”。A类?构造?A类还要构造吗?你莫名奇妙了。
事实上,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类继承默认构造函数时,找不到入口了,这就导致了如上的错误。如果要避免错误,有两种方法:1、在A类中添加一个无参数的构造函数。2、B类构造函数显式继承A类的构造函数。
事情已经解释的很清楚了,不过也许还有个小问题,就是:在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类的成员。虽然父类构造函数与子类构造函数的参数数量不一样,然而由于我们明确的指定了继承的关系,它们很好的完成了工作。