Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
Item 20 中提到使用泛型限制表達式可能會有過度限制的問題(e.g. 用戶端被強制要求實作介面),本節提出在方法限制輸入、輸出型別;消除過度限制的負面影響與增加設計彈型。
範例一:
public static class Example
{
public static T Add<T>( T left, T right, Func<T, T, T> AddFunc ) =>
AddFunc( left, right );
}
Client
int a = 6;
int b = 7;
int sum = Example.Add( a, b, ( x, y ) => x + y );
這是一個簡單的範例,Func<T,T,T> 讓外部可以自定義程式邏輯(相當於實作介面);好處是只有在需要呼叫該方法時才需要明確定義。
範例二:
public static class Utilities
{
public static IEnumerable<TOutput> zip<T1, T2, TOutput>
( IEnumerable<T1> left, IEnumerable<T2> right,
Func<T1, T2, TOutput> generartor )
{
IEnumerator<T1> leftSequence = left.GetEnumerator( );
IEnumerator<T2> rightSequence = right.GetEnumerator( );
while ( leftSequence.MoveNext( ) && rightSequence.MoveNext( ) )
yield return generartor( leftSequence.Current, rightSequence.Current );
leftSequence.Dispose( );
rightSequence.Dispose( );
}
}
Client
double[ ] xValues = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[ ] yValues = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
List<Point> values = new List<Point>(
Utilities.zip( xValues, yValues,
( x, y ) => new Point( x, y ) ) );
Note:此處用 T1, T2 而非僅用 T,原因在於整數型別的輸入在此情境下是合法的(int 可被隱式轉型成 double);當型別無法隱式轉型時,編譯器會跳出錯誤。
範例三:
public class Point
{
public double X { get; }
public double Y { get; }
public Point( TextReader reader )
{
var line = reader.ReadLine( );
var fields = line.Split( ',' );
if ( fields.Length != 2 )
throw new InvalidOperationException(
"Input format incorrect." );
if ( !double.TryParse( fields[ 0 ], out double valueX ) )
throw new InvalidOperationException(
"Could not parse X value." );
else
X = valueX;
if ( !double.TryParse( fields[ 1 ], out double valueY ) )
throw new InvalidOperationException(
"Could not parse Y value." );
else
Y = valueY;
}
}
public class InputCollection<T>
{
private readonly Func<TextReader, T> _readFunc;
private List<T> _thingsRead = new List<T>( );
public InputCollection( Func<TextReader, T> readFunc )
{
_readFunc = readFunc;
}
public void readFromStream( TextReader reader ) =>
_thingsRead.Add( _readFunc( reader ) );
public IEnumerable<T> Values => _thingsRead;
}
Client
var readValues = new InputCollection<Point>(
( inputStream ) => new Point( inputStream ) );
當客戶端想要從檔案串流讀取並轉換成 Point 時,只需呼叫 readFromStream 並帶入 reader 參數即可;讀取不同檔案需求也可滿足,同時也支援 foreach loop (IEnumerable<Point> Values)。回傳泛型型別也讓用戶端可自定義轉換方法。
結論:
1. 對於一般化的介面來說,明確定義實作介面是必要的(e.g. IEquatable<T>, IEnumerable<T>, IComparable<T>)。
2. 在只有特定情況使用的泛型方法,可以考慮使用泛型方法限制的方式實作。
1. 對於一般化的介面來說,明確定義實作介面是必要的(e.g. IEquatable<T>, IEnumerable<T>, IComparable<T>)。
2. 在只有特定情況使用的泛型方法,可以考慮使用泛型方法限制的方式實作。