警惕当类中有多个构造函数时的代码膨胀效应

有段时间没写博客了,不过我还是没有放松学习DotNet,这几天一直在看《.Net 框架程序设计》,收获很大!这本书不愧为.Net领域里的圣经,写得太好了,运思精深、鞭辟入里。我这是看的第一遍,而且才看了三分之一,就对.Net有了更深的理解,真的有一种豁然开朗的感觉,原来的很多问题,看了后才明白,原来就是这么回事呀。好东西不敢独享,接下来的几天我会在博客里把我的收获都写出来,和大家分享。

看这个类

class  MyClass
{
    
private int age = 22;
}

C#提供了一种简便的方法让我们初始化字段,如果我们用ildasm.exe查看这个类的构造函数IL代码,会发现C#把这行代码内联到了无参构造函数中,这样的确很方便,但却带来了一个我们不得不需要引起重视的问题。

看下面的代码

class  MyClass
{
    
private int age = 22;
    
private string name = "chengbo";

    
public MyClass() {
    }


    
public MyClass(int age) {
        
this.name = "cb";
    }

}

这个类提供了两个公有构造函数,其中第二个构造函数的IL如下

.method  public  hidebysig specialname rtspecialname 
        instance 
void   .ctor(int32 age) cil managed
{
  
// Code size       37 (0x25)
  .maxstack  2
  IL_0000:  ldarg.
0
  IL_0001:  ldc.i4.s   
22
  IL_0003:  stfld      int32 Chengbo.MyClass::age
  IL_0008:  ldarg.
0
  IL_0009:  ldstr      
"chengbo"
  IL_000e:  stfld      
string Chengbo.MyClass::name
  IL_0013:  ldarg.
0
  IL_0014:  call       instance 
void [mscorlib]System.Object::.ctor()
  IL_0019:  ldarg.
0
  IL_001a:  ldstr      
"cb"
  IL_001f:  stfld      
string Chengbo.MyClass::name
  IL_0024:  ret
}
  //  end of method MyClass::.ctor

可以看出,他首先内联了两个私有字段的代码,跟着调用了基类(System.Object类)的构造函数,再把cb赋值给name字段。

再看第一个,也就是那个无参的构造函数的IL

.method  public  hidebysig specialname rtspecialname 
        instance 
void   .ctor() cil managed
{
  
// Code size       26 (0x1a)
  .maxstack  2
  IL_0000:  ldarg.
0
  IL_0001:  ldc.i4.s   
22
  IL_0003:  stfld      int32 Chengbo.MyClass::age
  IL_0008:  ldarg.
0
  IL_0009:  ldstr      
"chengbo"
  IL_000e:  stfld      
string Chengbo.MyClass::name
  IL_0013:  ldarg.
0
  IL_0014:  call       instance 
void [mscorlib]System.Object::.ctor()
  IL_0019:  ret
}
  //  end of method MyClass::.ctor

同样,他也内联了两个私有字段的代码,跟着调用了基类(System.Object类)的构造函数。
这样,两个构造函数都内联了私有字段的赋值代码,无疑是增加了整个代码的长度,也就是所谓的“代码膨胀效应”。
那么应该怎么解决呢?可以像下面这样

class  MyClass
{
    
private int age;
    
private string name;

    
public MyClass() 
    
{
        age 
= 22;
        name 
= "chengbo";
    }


    
public MyClass(int age) : this()
    
{
        
this.name = "cb";
    }

}

第二个构造函数显式调用了第一个(无参)构造函数,然后再把cb赋值给name字段
看看他的IL

.method  public  hidebysig specialname rtspecialname 
        instance 
void   .ctor(int32 age) cil managed
{
  
// Code size       18 (0x12)
  .maxstack  2
  IL_0000:  ldarg.
0
  IL_0001:  call       instance 
void Chengbo.MyClass::.ctor()
  IL_0006:  ldarg.
0
  IL_0007:  ldstr      
"cb"
  IL_000c:  stfld      
string Chengbo.MyClass::name
  IL_0011:  ret
}
  //  end of method MyClass::.ctor

只是调用了第一个(无参的)构造函数,然后再把cb赋值给name字段,并没有内联私有字段赋值代码。

引用Jaffrey Richter在《Applied Microsoft .Net Framework Programming》中的一段话:

If you have several initialized instance fields and a lot of overloaded constructor methods, you should consider defining the fields without the initialization, creating a single constructor that performs the common initialization, and having each constructor explicitly call the common initialization constructor.This approach will reduce the size of the generated code.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值