Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
在現實情況下,常常會需要針對同一類別撰寫多種建構子,以應付不同的初始化需求。本章節提出利用建構子鏈或是預設參數建構子,來達到消除重覆初始化程式碼的目的。
1. 使用建構子鏈或是預設參數消除重覆的初始化邏輯。
建構子鏈:
public class MyClass
{
private List<int> _dataList;
private string _name;
public MyClass( )
: this( 0, string.Empty )
{
}
public MyClass( int initialCount )
: this( initialCount, string.Empty )
{
}
public MyClass( string name )
: this( 0, name )
{
}
public MyClass( int initialCount, string name )
{
_dataList = ( initialCount > 0 ) ?
new List<int>( initialCount ) :
new List<int>( );
_name = name;
}
}
預設參數:
public class MyClass2
{
private List<int> _dataList;
private string _name;
public MyClass2( int initialCount = 0, string name = "" )
{
_dataList = ( initialCount > 0 ) ?
new List<int>( initialCount ) :
new List<int>( );
_name = name;
}
}
預設參數在一般情況下比建構子鏈能更省程式碼(若參數數量為 2,建構子鏈需要定義 4 種不同的建構子才能滿足所有輸入需求)。即便如此,預設參數在使用上仍有些限制。
考慮以下程式碼:
public class MyClass3<T> where T : new() // Constrains by new( )
{
}
public class Client
{
// Complie Error, due to parameterless constructor is not defined.
private MyClass3<MyClass2> _myClass3 = new MyClass3<MyClass2>( );
}
為修復此錯誤,需在 MyClass2 加入無參數建構子的定義。
public class MyClass2
{
private List<int> _dataList;
private string _name;
public MyClass2( )
: this( 0, string.Empty )
{
}
public MyClass2( int initialCount = 0, string name = "" )
{
_dataList = ( initialCount > 0 ) ?
new List<int>( initialCount ) :
new List<int>( );
_name = name;
}
}
Note 2:預設參數值必須為編譯時期的常數(i.e. name 不能設定為 string.Empty, string.Empty 是靜態唯讀欄位)。
Note 3:承 Note 2,若組件修改預設參數值,客戶端程式也需重新編譯。
2. 使用建構子鏈消除 IL 重覆的物件初始化邏輯。
public class MyClass
{
private List<int> _dataList;
private string _name;
public MyClass( )
{
commonConstructor( 0, string.Empty );
}
public MyClass( int initialCount )
{
commonConstructor( initialCount, string.Empty );
}
public MyClass( string name )
{
commonConstructor( 0, name );
}
private void commonConstructor( int initialCount, string name )
{
_dataList = ( initialCount > 0 ) ?
new List<int>( initialCount ) :
new List<int>( );
_name = name;
}
}
以上程式碼乍看下沒有問題,但其實效率並不好;因為編譯器並不會自動幫我們消除重覆的物件初始化邏輯(父類別 object 初始化)。
編譯後的 IL Code:
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 程式碼大小 22 (0x16)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldc.i4.0
IL_000a: ldsfld string [mscorlib]System.String::Empty
IL_000f: call instance void CacheTest.MyClass::commonConstructor(int32,
string)
IL_0014: nop
IL_0015: ret
} // end of method MyClass::.ctor
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 initialCount) cil managed
{
// 程式碼大小 22 (0x16)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: ldsfld string [mscorlib]System.String::Empty
IL_000f: call instance void CacheTest.MyClass::commonConstructor(int32,
string)
IL_0014: nop
IL_0015: ret
} // end of method MyClass::.ctor
.method public hidebysig specialname rtspecialname
instance void .ctor(string name) cil managed
{
// 程式碼大小 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldc.i4.0
IL_000a: ldarg.1
IL_000b: call instance void CacheTest.MyClass::commonConstructor(int32,
string)
IL_0010: nop
IL_0011: ret
} // end of method MyClass::.ctor
每一種建構子都重覆呼叫了基類別的建構子,出現重覆的程式碼。
接著檢查建構子鏈的 IL Code,驗證是否能夠消除重覆的初始化邏輯。
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 程式碼大小 15 (0xf)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: ldsfld string [mscorlib]System.String::Empty
IL_0007: call instance void CacheTest.MyClass::.ctor(int32,
string)
IL_000c: nop
IL_000d: nop
IL_000e: ret
} // end of method MyClass::.ctor
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 initialCount) cil managed
{
// 程式碼大小 15 (0xf)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: ldsfld string [mscorlib]System.String::Empty
IL_0007: call instance void CacheTest.MyClass::.ctor(int32,
string)
IL_000c: nop
IL_000d: nop
IL_000e: ret
} // end of method MyClass::.ctor
.method public hidebysig specialname rtspecialname
instance void .ctor(string name) cil managed
{
// 程式碼大小 11 (0xb)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: ldarg.1
IL_0003: call instance void CacheTest.MyClass::.ctor(int32,
string)
IL_0008: nop
IL_0009: nop
IL_000a: ret
} // end of method MyClass::.ctor
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 initialCount,
string name) cil managed
{
// 程式碼大小 39 (0x27)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: ldc.i4.0
IL_000b: bgt.s IL_0014
IL_000d: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
IL_0012: br.s IL_001a
IL_0014: ldarg.1
IL_0015: newobj instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor(int32)
IL_001a: stfld class [mscorlib]System.Collections.Generic.List`1<int32> CacheTest.MyClass::_dataList
IL_001f: ldarg.0
IL_0020: ldarg.2
IL_0021: stfld string CacheTest.MyClass::_name
IL_0026: ret
} // end of method MyClass::.ctor
結果發現,確實消除重覆的基礎類別(object)初始化邏輯。
3. private function 無法設定唯讀欄位。
public class MyClass
{
private List<int> _dataList;
private readonly string _name;
public MyClass( )
{
commonConstructor( 0, string.Empty );
}
public MyClass( int initialCount )
{
commonConstructor( initialCount, string.Empty );
}
public MyClass( string name )
{
commonConstructor( 0, name );
}
private void commonConstructor( int initialCount, string name )
{
_dataList = ( initialCount > 0 ) ?
new List<int>( initialCount ) :
new List<int>( );
// Compile Error, changing the variable that is readonly where outside of
// constructor.
_name = name;
}
}
1. 使用建構子鏈或預設參數消除重覆的初始化邏輯。
2. C# 物件初始化的建構順序:
1. 靜態欄位初始化(第一次建構時執行)
2. 靜態建構子(第一次建構時執行)
3. 欄位初始化
4. 基類靜態欄位初始化(第一次建構時執行)
5. 基類靜態建構子(第一次建構時執行)
6. 基類欄位初始化
7. 基類建構子
8. 建構子