由一个Quiz想到的

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”,整个程序运行的结果又是什么?

要简单回到这道题是简单的,我的答案是:
 1 None.gif using  System;
 2 None.gif
 3 None.gif class  A
 4 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 5InBlock.gif    public A()
 6ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 7InBlock.gif        Console.WriteLine("A");
 8ExpandedSubBlockEnd.gif    }

 9ExpandedBlockEnd.gif}

10 None.gif
11 None.gif class  B
12 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
13InBlock.gif    public B()
14ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
15InBlock.gif        Console.WriteLine("B");
16ExpandedSubBlockEnd.gif    }

17ExpandedBlockEnd.gif}

18 None.gif
19 None.gif class  C : A
20 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
21InBlock.gif    public C()
22ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
23InBlock.gif        Console.WriteLine("C");
24ExpandedSubBlockEnd.gif    }

25InBlock.gif    B b = new B();
26ExpandedBlockEnd.gif}

27 None.gif
28 None.gif class  Test
29 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
30InBlock.gif    public static void Main()
31ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
32InBlock.gif        C c = new C();
33InBlock.gif        
34ExpandedSubBlockEnd.gif    }

35ExpandedBlockEnd.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 None.gif .method  public  hidebysig specialname rtspecialname 
 2 None.gif        instance  void   .ctor() cil managed
 3 ExpandedBlockStart.gifContractedBlock.gif dot.gif {
 4InBlock.gif  // 代码大小       28 (0x1c)
 5InBlock.gif  .maxstack  2
 6InBlock.gif  IL_0000:  ldarg.0
 7InBlock.gif  IL_0001:  newobj     instance void B::.ctor()
 8InBlock.gif  IL_0006:  stfld      class B C::b
 9InBlock.gif  IL_000b:  ldarg.0
10InBlock.gif  IL_000c:  call       instance void A::.ctor()
11InBlock.gif  IL_0011:  ldstr      "C"
12InBlock.gif  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
13InBlock.gif  IL_001b:  ret
14ExpandedBlockEnd.gif}
  //  end of method C::.ctor

以上是C构造函数的IL代码,我们可以清楚地看到首先创建可B的实例b,当然同时调用了B的构造函数,然后把b值存为C的对象的一个字段,然后调用A的构造函数,然后在调用WriteLine方法。

可能会有人对B b = new B()是内联初始化不太理解,那么我再添加以行代码int i = 1;到后面,使C类变为:
None.gif class  C : A
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
public C()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        Console.WriteLine(
"C");
ExpandedSubBlockEnd.gif    }

InBlock.gif    B b 
= new B();
InBlock.gif    
int i = 1;
ExpandedBlockEnd.gif}

None.gif

那么IL就变成:
None.gif .method  public  hidebysig specialname rtspecialname 
None.gif        instance 
void   .ctor() cil managed
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
// 代码大小       35 (0x23)
InBlock.gif
  .maxstack  2
InBlock.gif  IL_0000:  ldarg.
0
InBlock.gif  IL_0001:  newobj     instance 
void B::.ctor()
InBlock.gif  IL_0006:  stfld      
class B C::b
InBlock.gif  IL_000b:  ldarg.
0
InBlock.gif  IL_000c:  ldc.i4.
1
InBlock.gif  IL_000d:  stfld      int32 C::i
InBlock.gif  IL_0012:  ldarg.
0
InBlock.gif  IL_0013:  call       instance 
void A::.ctor()
InBlock.gif  IL_0018:  ldstr      
"C"
InBlock.gif  IL_001d:  call       
void [mscorlib]System.Console::WriteLine(string)
InBlock.gif  IL_0022:  ret
ExpandedBlockEnd.gif}
  //  end of method C::.ctor

你看,这回比较清楚了吧,字段的初始化是在前面,同时说明了一个问题,就是,要避免内联初始化字段,这样会增加代码尺寸,把初始化放构造器中去进行, 这里不再细述。

转载于:https://www.cnblogs.com/wdxinren/archive/2005/05/17/157013.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值