CSDN社区C#版有人出基础题,我没事也做一下练练,今天做到一道题是这样的:
(原题链接:http://community.csdn.net/Expert/topic/3918/3918948.xml?temp=.2294428)
编写一个控制台应用程序,完成下列功能,并回答提出的问题。
1.创建一个类A,在构造函数中输出“A”,再创建一个类B,在构造函数中输出“B”。
2.从A继承一个名为C的新类,并在C内创建一个成员B。不要为C创建构造函数。
3.在Main方法中创建类C的一个对象,写出运行程序后输出的结果。
4.如果在C中也创建一个构造函数输出“C”,整个程序运行的结果又是什么?
要简单回到这道题是简单的,我的答案是:
跟作者的参考答案是一致的,但是作者在结帖的时候并没有回答有人提出的一个问题:问什么是这样的输出顺序呢?
我就自己想了一下。首先看输出解结果“B”是调用B类的实例构造函数输出的,“A”是调用A类的构造函数输出的,“C”调用C类的构造函数输出的。
其中B类作为一个字段出现在C类中,而C类继承A类。那么,我猜,在主函数中构造C对象,首先初始化C类的字段,然后调用祖先类的构造函数,再实现C类自己构造函数中的代码,这样就解释了这种顺序。
那么这种猜想成不成立呢?
首先初始化C类的字段可能会有些疑惑,为什么非要先初始化类的字段呢?这里就扯到另外一个问题:字段的内联初始化是在哪里初始化的(这里的内联是指声明字段时同时进行初始化赋值,比如public int i = 1,而不是public in i),在《.NET框架程序设计》9.1节实例构造器中有解释,这里我只说结果,就是内联初始化实际上是在类型的构造器中完成的。那么这样看来,初始化B类对象b跟C中的Console.WriteLine()方法都发生在C类的构造器中,其实调用父类A的构造器也发生在C的构造器,至于为什么非要把内联初始化字段放在最前面,我就不清楚了,还请大家指点(可能有什么先搞定字段,再搞方法的规则吧,哈哈)。不过这个顺序是没错的,有IL作证:
以上是C构造函数的IL代码,我们可以清楚地看到首先创建可B的实例b,当然同时调用了B的构造函数,然后把b值存为C的对象的一个字段,然后调用A的构造函数,然后在调用WriteLine方法。
可能会有人对B b = new B()是内联初始化不太理解,那么我再添加以行代码int i = 1;到后面,使C类变为:
class C : A
{
public C()
{
Console.WriteLine("C");
}
B b = new B();
int i = 1;
}
![](/Images/OutliningIndicators/None.gif)
那么IL就变成:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 35 (0x23)
.maxstack 2
IL_0000: ldarg.0
IL_0001: newobj instance void B::.ctor()
IL_0006: stfld class B C::b
IL_000b: ldarg.0
IL_000c: ldc.i4.1
IL_000d: stfld int32 C::i
IL_0012: ldarg.0
IL_0013: call instance void A::.ctor()
IL_0018: ldstr "C"
IL_001d: call void [mscorlib]System.Console::WriteLine(string)
IL_0022: ret
} // end of method C::.ctor
你看,这回比较清楚了吧,字段的初始化是在前面,同时说明了一个问题,就是,要避免内联初始化字段,这样会增加代码尺寸,把初始化放构造器中去进行, 这里不再细述。
(原题链接:http://community.csdn.net/Expert/topic/3918/3918948.xml?temp=.2294428)
编写一个控制台应用程序,完成下列功能,并回答提出的问题。
1.创建一个类A,在构造函数中输出“A”,再创建一个类B,在构造函数中输出“B”。
2.从A继承一个名为C的新类,并在C内创建一个成员B。不要为C创建构造函数。
3.在Main方法中创建类C的一个对象,写出运行程序后输出的结果。
4.如果在C中也创建一个构造函数输出“C”,整个程序运行的结果又是什么?
要简单回到这道题是简单的,我的答案是:
1
using System;
2![](/Images/OutliningIndicators/None.gif)
3
class A
4
{
5
public A()
6
{
7
Console.WriteLine("A");
8
}
9
}
10![](/Images/OutliningIndicators/None.gif)
11
class B
12
{
13
public B()
14
{
15
Console.WriteLine("B");
16
}
17
}
18![](/Images/OutliningIndicators/None.gif)
19
class C : A
20
{
21
public C()
22
{
23
Console.WriteLine("C");
24
}
25
B b = new B();
26
}
27![](/Images/OutliningIndicators/None.gif)
28
class Test
29
{
30
public static void Main()
31
{
32
C c = new C();
33
34
}
35
}
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/None.gif)
4
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
9
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
10
![](/Images/OutliningIndicators/None.gif)
11
![](/Images/OutliningIndicators/None.gif)
12
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
17
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
18
![](/Images/OutliningIndicators/None.gif)
19
![](/Images/OutliningIndicators/None.gif)
20
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
21
![](/Images/OutliningIndicators/InBlock.gif)
22
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
23
![](/Images/OutliningIndicators/InBlock.gif)
24
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
25
![](/Images/OutliningIndicators/InBlock.gif)
26
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
27
![](/Images/OutliningIndicators/None.gif)
28
![](/Images/OutliningIndicators/None.gif)
29
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
30
![](/Images/OutliningIndicators/InBlock.gif)
31
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
32
![](/Images/OutliningIndicators/InBlock.gif)
33
![](/Images/OutliningIndicators/InBlock.gif)
34
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
35
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
跟作者的参考答案是一致的,但是作者在结帖的时候并没有回答有人提出的一个问题:问什么是这样的输出顺序呢?
我就自己想了一下。首先看输出解结果“B”是调用B类的实例构造函数输出的,“A”是调用A类的构造函数输出的,“C”调用C类的构造函数输出的。
其中B类作为一个字段出现在C类中,而C类继承A类。那么,我猜,在主函数中构造C对象,首先初始化C类的字段,然后调用祖先类的构造函数,再实现C类自己构造函数中的代码,这样就解释了这种顺序。
那么这种猜想成不成立呢?
首先初始化C类的字段可能会有些疑惑,为什么非要先初始化类的字段呢?这里就扯到另外一个问题:字段的内联初始化是在哪里初始化的(这里的内联是指声明字段时同时进行初始化赋值,比如public int i = 1,而不是public in i),在《.NET框架程序设计》9.1节实例构造器中有解释,这里我只说结果,就是内联初始化实际上是在类型的构造器中完成的。那么这样看来,初始化B类对象b跟C中的Console.WriteLine()方法都发生在C类的构造器中,其实调用父类A的构造器也发生在C的构造器,至于为什么非要把内联初始化字段放在最前面,我就不清楚了,还请大家指点(可能有什么先搞定字段,再搞方法的规则吧,哈哈)。不过这个顺序是没错的,有IL作证:
1
.method public hidebysig specialname rtspecialname
2
instance void .ctor() cil managed
3
{
4
// 代码大小 28 (0x1c)
5
.maxstack 2
6
IL_0000: ldarg.0
7
IL_0001: newobj instance void B::.ctor()
8
IL_0006: stfld class B C::b
9
IL_000b: ldarg.0
10
IL_000c: call instance void A::.ctor()
11
IL_0011: ldstr "C"
12
IL_0016: call void [mscorlib]System.Console::WriteLine(string)
13
IL_001b: ret
14
} // end of method C::.ctor
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/InBlock.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
以上是C构造函数的IL代码,我们可以清楚地看到首先创建可B的实例b,当然同时调用了B的构造函数,然后把b值存为C的对象的一个字段,然后调用A的构造函数,然后在调用WriteLine方法。
可能会有人对B b = new B()是内联初始化不太理解,那么我再添加以行代码int i = 1;到后面,使C类变为:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
![](/Images/OutliningIndicators/None.gif)
那么IL就变成:
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
你看,这回比较清楚了吧,字段的初始化是在前面,同时说明了一个问题,就是,要避免内联初始化字段,这样会增加代码尺寸,把初始化放构造器中去进行, 这里不再细述。