轉型和型別轉換是將運算式從一種型別改成另一種型別的兩種不同方式。
C# 文件將型別轉換歸類為使用as運算子或is運算子。而轉型則定義為使用轉型運算子。
在 C# 中,很多時候在數值和類別階層以外的情況,你都必須呼叫「目標」型別的某個成員來進行轉換,例如呼叫Int32.Parse()將字串轉成整數,或是呼叫「來源」型別的成員,例如object.ToString()。JavaScript 開發者和其他動態語言的開發者都應該留意這一點。
請注意,還有隱含和明確的轉型運算子(在 operator-overloading 中討論過),可以讓你的自訂型別實現相當自由的轉型。
C# 的型別系統比 C 或 JavaScript 稍微嚴格一些,因此轉型的操作也受到較多限制。只要「目標」型別能保留「來源」型別值的範圍和正負號,隱含轉型就會在兩個數值型別之間發生。請注意文件中關於轉換成實數時可能遺失精確度的例外說明。
隱含轉型不需要任何特殊的語法來表示。例如:
int myInt = 1729;
long myLong = myInt;
數值(或字串)運算式無法隱含轉換成bool。基底類別庫(BCL)為此提供了Convert.ToBoolean()。
型別為char的運算式可以隱含轉型成int。反方向的轉型則必須是明確的。並非所有int的值都是有效的 UTF-16 字元。
當數值型別無法隱含轉型時,你通常可以使用明確轉型運算子。
如果被轉型的值因為寬度不足或正負號衝突而無法用「目標」型別表示,那麼在整數的情況下可能會擲回溢位例外,而在 float 和 double 的情況下,「目標」型別的變數則可能得到Infinity這個值。
型別為int的運算式可以明確轉型成char。這可能會產生無效的char。
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
float largeFloat = float.MaxValue;
float smallFloat = 17.29f;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
int from_char = 'a'; // 96
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
int from_smallFloat = (int) smallFloat; // 17
int from_largeFloat = (int) largeFloat; // -2147483648 or OverflowException is thrown (if checked)
char from_intc = (char) 32; // ' '
char from_invalid_int = (char) 0xdcbf; // invalid char - no exception thrown
// no cast available
int fromString = Int32.Parse("42"); // 42
string toString = largeInt.ToString(); // "2147483647"
int fromString_bad = Int32.Parse("forty two"); // FormatException is thrown
關於 checked 關鍵字,請參閱這篇文章。
任何型別都可以隱含轉換成它的基底類別或介面。
IList<int> ll = new List<int>();
轉型運算子可以用來從基底類別轉換成衍生類別。
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
如果轉型失敗,原因是被轉型的運算式型別與要轉型成的型別不相關,就會擲回InvalidCastException。
面對這些型別轉換,更常見的做法是使用is關鍵字:
interface IFoo { int Bar(); }
class Foo : IFoo { public int Bar() { return 42; }}
Object r = new Random();
IFoo ifoo = new Foo();
Object o = new Foo();
int result = 1729;
if (ifoo is Foo foo)
{
result = foo.Bar();
}
// result == 42
result = 1729;
if (o is Foo foo2)
{
result = foo2.Bar();
}
// result == 42
result = 1729;
if (r is Foo foo3)
{
result = foo3.Bar();
}
// result == 1729
as關鍵字的功能和is類似,例如var foo = ifoo as Foo;。在這個例子中,如果ifoo的型別不是Foo,foo就會是null;否則ifoo會被指定給它。
型別可以定義自己的自訂明確和隱含轉型運算子。
BCL 中明確和隱含轉型的例子,就是BigInteger結構與其他數值型別之間的相互轉換。
typeof
如果你需要偵測物件的確切型別,那麼is可能稍微過於寬鬆,因為它會把物件轉換成某個類別或它的任何基底類別。在這種情況下,typeof和Object.GetType()就是解決方案。
object o = new List<int>();
o is ICollection<int> // true
o.GetType() == typeof(ICollection<int>) // false
o is List<int> // true
o.GetType() == typeof(List<int>) // true