Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
在 C# 這種強型別的語言中,有時需要轉型操作(e.g. object 轉型成其他型別)。作者在本章節建議使用 as 或 is 用以轉型,以下針對 as operator 和 Cast 做一些比較。
1. 使用 as operator 比 Cast 效能更好且語意更簡潔。
Version one :
object o = Factory.GetObject( );
MyType t = o as MyType;
if ( t != null )
{
// work with t, it's MyType.
}
else
{
// report the failure.
}
Version two :
object o = Factory.GetObject( );
try
{
MyType t;
t = ( MyType ) o;
if ( t != null )
{
// work with t, it's MyType.
}
else
{
// report o is null, all of null value could be casted to another
// reference type that will return null.
}
}
catch ( InvalidCastException )
{
// report the conversion failure.
}
顯然 Version one 程式碼較為簡短,利用 try catch block 控制流程除了消耗額外資源;也會造成程式碼閱讀較為困難。使用 as operator 轉型只需檢查回傳值是否為 null 值(強制轉型即便轉型成功也需要做相同檢查,因為所有的 object null 值皆可轉型成其他參考型別並回傳 null)。
2. Cast 可自定義轉型,而 as operator 不行。
public class SecondType
{
private MyType _value;
// Other detail elided.
// Conversion operator.
// This converts a SecondType to a MyType.
public static implicit operator MyType( SecondType t )
{
return t._value;
}
}
Version one :
object o = Factory.GetObject( );
// o is SecondType.
MyType t = o as MyType; // Fails, o is not MyType.
if ( t != null )
{
// work with t, it's MyType.
}
else
{
// report the failure.
}
Version two :
object o = Factory.GetObject( );
try
{
MyType t;
t = ( MyType ) o; // Fails, o is not MyType.
if ( t != null )
{
// work with t, it's MyType.
}
else
{
// report t is null, all of object type null value could be casted to
// another reference type that will return null.
}
}
catch ( InvalidCastException )
{
// report the conversion failure.
}
兩種情況皆會轉型失敗。原因在於 as operator 只關心在執行階段 o 是否為 MyType;而上述情況很顯然 o 不是 MyType,所以回傳 null。而 Version two 雖然有自定義的轉型方法,但只在編譯階段有效;也就是說 o 在編譯階段並非 SecondType,故執行後 CLR 會依照預設方式執行(檢查 o 在執行階段是否為 MyType),最後跳出例外。
而要在 Version two 成功執行自定義轉型方法,需要將程式改寫。
Version three :
object o = Factory.GetObject( );
SecondType st = o as SecondType; // cast o to SecondType by as operator.
try
{
MyType t;
t = ( MyType ) st; // pass, st could be casted to MyType by definition.
if ( t != null )
{
// work with t, it's MyType.
}
else
{
// report t is null, null value of object type could be casted to
// another reference type that will return null.
}
}
catch ( InvalidCastException )
{
// report the conversion failure.
}
3. 使用 as operator 可得到較為一致的結果。
t = ( MyType )st; // 宣告不同 st 型別會有不同結果。
t = st as MyType; // as operator 只關注執行階段 st 是否能轉型成功。
4. as operator 只能使用在轉型為參考型別(可為 null 型別),而 Cast 無此限制。
object o = Factory.GetValue( );
int i = o as int // Does not compile.
由於 as operator 在轉型失敗時會回傳 null;而 int 無法為 null,故編譯失敗。
這樣的問題可以使用 Nullable<T> 解決。
object o = Factory.GetValue( );
var i = o as int?; // it's work whenever o is int or int?.
if ( i != null )
Console.WriteLine( i.Value );
5. foreach loop 使用 Cast 而非 as operator 讓轉型更加彈性(不限制轉為參考型別)。
public void UseCollectionV1( IEnumerable theCollection )
{
foreach ( MyType t in theCollection )
t.DoStuff( );
}
等同於
public void UseCollectionV2( IEnumerable theCollection )
{
IEnumerator it = theCollection.GetEnumerator( );
while ( it.MoveNext( ) )
{
MyType t = ( MyType ) it.Current;
t.DoStuff( );
}
}
1. 在一般情況下,使用 as operator 比 Cast 更能得到一致的結果、更好的可讀性與效能。
2. 在繼承體系中,無論 as operator 或 Cast 皆能從子類別轉型為父類別;若要進一步精確的比較型別,可使用 object.GetType( )。