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 MyBase
{
}
public interface IMessageWritter
{
void writeMessage( );
}
public class MyDerived : MyBase, IMessageWritter
{
void IMessageWritter.writeMessage( ) =>
Debug.WriteLine( "Inside MyDerived.writeMessage." );
}
public class AnotherType : IMessageWritter
{
public void writeMessage( ) =>
Debug.WriteLine( "Inside AnotherType.writeMessage." );
}
2. 定義輸入型別為基類、泛型、介面等 writeMessage 方法多載。
public static void writeMessage( MyBase b ) =>
Debug.WriteLine( "Inside writreMessage( MyBase )." );
public static void writeMessage<T>( T obj )
{
Debug.Write( "Inside writeMessage<T>( T ) : " );
Debug.WriteLine( obj.ToString( ) );
}
public static void writeMessage( IMessageWritter obj )
{
Debug.Write( "Inside writeMessage( IMessageWritter ) : " );
obj.writeMessage( );
}
3. 撰寫測試方法。
var d = new MyDerived( );
Debug.WriteLine( "Calling Item24.writeMessage" );
writeMessage( d );
Debug.WriteLine( string.Empty );
Debug.WriteLine( "Calling through IMessageWritter interface." );
writeMessage( ( IMessageWritter ) d );
Debug.WriteLine( string.Empty );
Debug.WriteLine( "Cast to base object" );
writeMessage( ( MyBase ) d );
Debug.WriteLine( string.Empty );
Debug.WriteLine( "Another Type test :" );
var anobj = new AnotherType( );
writeMessage( anobj );
Debug.WriteLine( string.Empty );
Debug.WriteLine( "Cast to IMessageWritter." );
writeMessage( ( IMessageWritter ) anobj );
輸出:
// 呼叫泛型輸入方法。
Calling Item24.writeMessage
Inside writeMessage<T>( T ) : Item24.MyDerived
// 呼叫介面輸入方法。
Calling through IMessageWritter interface.
Inside writeMessage( IMessageWritter ) : Inside MyDerived.writeMessage.
// 呼叫基類輸入方法。
Cast to base object
Inside writreMessage( MyBase ).
// 呼叫泛型輸入方法。
Another Type test :
Inside writeMessage<T>( T ) : Item24.AnotherType
// 呼叫介面輸入方法。
Cast to IMessageWritter.
Inside writeMessage( IMessageWritter ) : Inside AnotherType.writeMessage.
泛型方法將會自動在編譯期選擇最適當的方法。隨著顯式的轉換輸入型別,其輸出結果有會有所不同。而在編譯時期就決定呼叫方法,省去了執行階段的額外判斷。
4. 避免針對基類或介面定義特化的泛型方法。
public static void writeMessage2<T>( T obj )
{
if ( ( object ) obj is MyBase myBase ) // It's a C# 7.0 problem.
writeMessage( myBase );
else if ( obj is IMessageWritter iMessageWritter )
writeMessage( iMessageWritter );
else
{
Write( "Inside writeMessage<T>( T ) : " );
WriteLine( obj.ToString( ) );
}
}
應避免這樣的寫法,在執行階段需要額外檢查輸入型別;且無法在早期編譯階段就決定呼叫方法,增加後續除錯風險。然而並非完全不可在泛型方法中判斷輸入型別,Item 19 中在執行階段判斷輸入型別,以達到高效率的初始化方式是可行的。
1. 若要特化基類或介面的泛型方法,需在方法中判斷所有子類別或實作類別。
2. 避免 1. 的設計方式,減少執行階段額外操作。