Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
何時該使用泛型類別或泛型方法?本節提出一個判斷依據。
準則:盡量使用泛型方法,除非類別擁有泛型成員或實作泛型介面。
範例一:
public static class Utils<T>
{
public static T max( T left, T right ) =>
Comparer<T>.Default.Compare( left, right ) < 0 ?
right : left;
public static T min( T left, T right ) =>
Comparer<T>.Default.Compare( left, right ) < 0 ?
left : right;
}
private void test1( )
{
double d1 = 4, d2 = 5;
double max = Utils<double>.max( d1, d2 );
string foo = "foo", bar = "bar";
string min = Utils<string>.min( foo, bar );
}
如此設計雖然不會出錯,但每次呼叫方法時都必須指定泛型類別(多餘的程式碼);且在這個例子裡,double 已經有內嵌的 Math.Max 及 Math.Min 方法,目前寫法並非最有效率的方式。
改良後程式碼:
public static class Utils
{
public static T max<T>( T left, T right ) =>
Comparer<T>.Default.Compare( left, right ) < 0 ?
right : left;
public static double max( double left, double right ) =>
Math.Max( left, right );
public static T min<T>( T left, T right ) =>
Comparer<T>.Default.Compare( left, right ) < 0 ?
left : right;
public static double min( double left, double right ) =>
Math.Min( left, right );
}
private void test2( )
{
double d1 = 4, d2 = 5;
double max = Utils.max( d1, d2 );
string foo = "foo", bar = "bar";
string min = Utils.min( foo, bar );
double? d3 = 12, d4 = null;
double? max2 = Utils.max( d3, d4 );
}
我們將指定輸入型別的責任從類別移到方法;如此一來,讓編譯器在編譯階段自動選擇最適合的執行方法,也省去不必要的程式碼。
範例二:
public class CommaSeparatedListBuilder
{
private StringBuilder _storage = new StringBuilder( );
public void add<T>( IEnumerable<T> items )
{
foreach ( T item in items )
{
if ( _storage.Length > 0 )
_storage.Append( ", " );
_storage.Append( @"\" );
_storage.Append( item.ToString( ) );
_storage.Append( @"\" );
}
}
public override string ToString( ) =>
_storage.ToString( );
}
若是將泛型定義在類別,則一個 CommaSeparatedListBuilder 物件就只能對應到一種型別;若有多型別輸入的需求,就要初始化多個物件。在這個情境下,使用泛型方法是合理的選擇。
結論:
1. 若類別含有泛型成員或實作泛型介面,則將泛型定義在類別。
2. 除了 1. 的情況,應將泛型定義在方法;增加重用性與效率。
1. 若類別含有泛型成員或實作泛型介面,則將泛型定義在類別。
2. 除了 1. 的情況,應將泛型定義在方法;增加重用性與效率。