何謂型別轉換
資料在進行型別轉換時,可分成明確轉換或隱含轉換二種。
- 明確轉換(explicit conversion):明確指定轉換的型別。
- 隱含轉換(implicit conversion):由系統自動進行不同型別資料的轉換。
//明確轉型 long a = System.Convert.ToInt64(10000); //明確轉型 byte c = (byte) a; //long 轉 byte 可能會造成資料丟失 //隱含轉型 a = c; //byte 轉 long
隱含轉換的過程,可能造成下面二種情況:
- 擴展轉換(widening conversion):隱含轉換時,目的型別精準度大於原始型別。
- 縮小轉換(narrowing conversion):隱含轉換時,目的型別精準度小於原始型別。
VB預設二種情況都是允許的,C#則禁止縮小轉換。 VB若要關閉隱含轉換,可在程式碼檔案最前面加上 Option Strict On。 或者在[專案]->[屬性]->[編譯],將整個專案的 Option Strict 設為 On。
int i = 10; double d = 5.5; d = i; // 擴展轉換 //i = d; // 縮小轉換: C#不允許縮小轉換,所以會產生 double 不能隱含轉換為 int 的錯誤. i = (int)5.5; // explicit conversion , may lost some information
何謂 Boxing 與 Unboxing
- Boxing : Value Type to Reference Type, implicit convertion.
- Unboxing : Reference Type to Value Type, explicit convertion.
int i = 123; object obj = i; //boxing會自動隱含轉換
object obj = 123; int i = (int)obj; //unboxing 動作必須明確宣告轉換型別,無法自動隱含轉換 int j = obj; //這一行會錯誤。
下面這個例子,第 4 和第 8 行會發生轉換錯誤。原因?
int no1 = 10; object obj1 = no1; int no2 = (int)obj1; short no3 = (short)obj1; short no4 = 10; object obj2 = no4; int no5 = (int)obj2; short no6 = (short)obj2;
- 行2:obj1 boxing 一個 int 的資料。
- 行3:no2 將 obj1 中的資料 unboxing 回 int.
- 行4:obj1 中的資料為一個 int 型別資料,無法 unboxing 成 short 型別。
- 行8:obj2 中的資料為一個 short 型別資料,無法 unboxing 成 int 型別。
如何實作自訂型別的轉換 (Conversion)
若要設計自訂型別的轉型,通常必須根據想要轉換的型別使用不同的技巧。例如以下幾種方式:
- 覆寫 ToString:提供將其他型別轉換為字串的功能。
- 覆寫 Parse:提供將字串轉換為數字型別的功能。
- 定義轉換運算子:可執行數值之間的轉換功能。
- 加入 System.IConvertible 介面:在自訂型別中,實作各種型別的轉換方法。
ToString & Parse
- ToString:這個方法是覆寫自 Object 的 ToString 方法,可用來將數值型態的執行個體,轉換成對等的字串。
- Parse:每個數值型別都具有靜態的 Parse 方法,可用來將字串轉換成本身的數值型別。
int iNum = 10; double dNum = 5.5; string str; str = iNum.ToString(); //將int轉換為字串。 dNum = double.Parse(str); //將字串轉換為double。 iNum = int.Parse(str); //將字串轉換為int。
無參數的 ToString 方法是覆寫自 Object.ToString 。 通常.NET 內建的型別中,也會自訂其它多載的 ToString 方法,以應付不同需求狀況。例如底下為 DateTime 型別4個多載方法的樣式:
public override string ToString(); public string ToString(IFormatProvider provider); public string ToString(string format); public string ToString(string format, IFormatProvider provider);
DateTime theDate = new DateTime(2012, 7, 20); Console.WriteLine(theDate.ToString()); // 2012/7/20 上午 12:00:00 Console.WriteLine(theDate.ToString("d")); // 2012/7/20 //使用委內瑞拉的 CultureInfo 來顯示日期 CultureInfo esVE = new CultureInfo("es-VE"); Console.WriteLine(theDate.ToString("d", esVE)); // 20/07/2012 //使用克羅埃西亞的 DateTimeFormatInfo 來顯示日期 DateTimeFormatInfo fmt = new CultureInfo("hr-HR").DateTimeFormat; Console.WriteLine(theDate.ToString("d", fmt)); // 20.7.2012 Console.WriteLine(theDate.ToString(fmt.ShortDatePattern)); // 20.7.2012
int num = 4567; Console.WriteLine(num.ToString()); // 4567 Console.WriteLine(num.ToString("d")); // 4567 Console.WriteLine(num.ToString("d6")); // 004567 Console.WriteLine(num.ToString("C")); // NT$4,567.00 Console.WriteLine(num.ToString("N")); // 4,567.00 //使用大陸的 CultureInfo CultureInfo zhCN = new CultureInfo("zh-CN"); Console.WriteLine(num.ToString("C", zhCN)); // ¥4,567.00 //使用克羅埃西亞的 NumberFormatInfo NumberFormatInfo fmt = new CultureInfo("hr-HR").NumberFormat; Console.WriteLine(num.ToString("C", fmt)); // 4.567,00 kn
轉換運算子 (Conversion operator)
轉換運算子就是在自訂類別中直接宣告型別的轉換,該自訂型別可與其他型別或.NET型別互相轉換。
轉換運算子有二種屬性, explicit 或 implicit。詳情請參考:使用轉換運算子 (C# 程式設計手冊)
隱含轉換 vs 明確轉換
- 宣告為隱含的轉換運算子(implicit),會在必要時自動發生,也就是客戶端不需要動作就會自行轉換。
此種轉換使用在資料不會失去精準度時使用,例如 int => double。 - 宣告為明確的轉換運算子(explicit),需要呼叫轉型,也就是客戶端得使用強制轉型才會進行轉換動作。
此種轉換使用在容易失去精準度時使用,例如 double => int。
使用轉換運算子時,要注意以下幾點︰
- 所有轉換必須宣告為 static。
- 轉換運算子的宣告方法就和運算子一樣,並且以轉換成的型別來命名。
- 轉換運算子的宣告,轉換的結果或傳入的參數,其中一個必須是轉換型別。
使用 operator 關鍵字來建立使用者定義轉換的語法如下:
//目標型別或來源型別,其中一個必須是轉換型別 public static implicit operator 目標型別 ( 來源型別名稱 ) public static explicit operator 目標型別 ( 來源型別名稱 )
public static implicit operator float (myType _type) // 定義隱含轉換運算子,允許 myType 型別隱含轉換成 float 型別 public static explicit operator int (myType _type) // 定義明確轉換運算子, myType 型別必須明確轉換成 int 型別 public static explicit operator myType (int _value) public static implicit operator myType (float _value)
下面這個例子中的CTemp、FTemp是二個自訂型別,程式碼示範如何利用轉換運算子做型別轉換。若我們需求程式可以達到:
1. 二者都可以和 float 型別做隱含轉換。
2. FTemp 可隱含轉換成 CTemp ; CTemp 須明確轉換成 FTemp。
public abstract class Temperature { public float Temp { get; set; } public Temperature(float temp) { this.Temp = temp; } public override string ToString() { return Temp.ToString("0.00"); } } public class CTemp : Temperature { //將建構子設為 private,代表無法用 new 關鍵字new出 CTemp 型別 private CTemp(float temp) : base(temp) { } public static implicit operator float(CTemp c) //隱含轉換 (將CTemp轉成float) { return c.Temp; } public static implicit operator CTemp(float temp) //隱含轉換 (將float轉成CTemp) { return new CTemp(temp); } public static implicit operator CTemp(FTemp f) //隱含轉換 (將FTemp轉成CTemp) { return (((f.Temp - 32) * 5) / 9); } public static explicit operator FTemp(CTemp c) //明確轉換 (將CTemp轉成FTemp) { return (((c.Temp * 9) / 5) + 32); } } public class FTemp : Temperature { private FTemp(float temp) : base(temp) { } public static implicit operator FTemp(float temp) { return new FTemp(temp); } public static implicit operator float(FTemp f) { return f.Temp; } } CTemp tempC1, tempC2, tempC3; FTemp tempF = 95; tempC1 = 28.563f; // float 允許隱含轉換成 CTemp tempC2 = 25; // int 允許隱含轉換成 CTemp tempC3 = tempF; // FTemp 允許隱含轉換成 CTemp Console.WriteLine(tempC1.ToString()); // 28.56 Console.WriteLine(tempC2.ToString()); // 25.00 Console.WriteLine(tempC3.ToString()); // 35.00 //tempF = tempC3; // CTemp 不能隱含轉換成 FTemp tempF = (FTemp)tempC3; // 必須明確轉換成 FTemp Console.WriteLine(tempF.ToString()); // 95.00
實作 System.IConvertible 介面
System.IConvertible 介面是用來定義自訂型別轉換成具有等值的 CLR 型別 (如 Boolean、Byte、Int16 ...) 。
若要實作 System.IConvertible 介面,可以將 IConvertible 介面加入到型別定義中,然後實作該介面。
TypeA a = 68; string s = a.ToString(); byte be = Convert.ToByte(a); bool bl = Convert.ToBoolean(a); int i = Convert.ToInt16(a); class TypeA : IConvertible { protected int Value; public static implicit operator TypeA(int arg) { TypeA res = new TypeA(); res.Value = arg; return res; } public override string ToString() { return this.Value.ToString(); } public bool ToBoolean(IFormatProvider provider) { if (Value > 0) return true; else return false; } public byte ToByte(IFormatProvider provider) { if (Value < 255) return (byte)Value; else return 0; } public short ToInt16(IFormatProvider provider) { if (Value < 32767) return (short)Value; else return 0; } }
實作 System.IFormattable 介面
IFormattable 介面是用來自訂型別的字串顯示樣式,它會根據「格式字串」和「格式提供者」這二個參數,將物件轉換成其字串表示。
格式字串:通常用來定義外觀。例如,.NET Framework 定義了下列幾種格式字串:
格式提供者:通常用來定義字串使用的符號。例如,.NET Framework 會定義三種格式提供者:
- System.Globalization.CultureInfo 類別:文化特性類別,其中包含了 NumberFormatInfo 或 DateTimeFormatInfo 。
- System.Globalization.NumberFormatInfo:這個類別用來表示某個文化特性下的數字格式。
- System.Globalization.DateTimeFormatInfo:這個類別用來表示某個文化特性下的日期和時間格式
下面例子,我們簡單設計一個用來表示攝氏溫度的型別,並繼承 IFormattable 介面。 繼承 IFormattable 介面就必須實作 ToString 方法,我們在這個方法中,依不同的參數值顯示不同的溫度格式。
public class MyTemperature : IFormattable { private float m_Temp; public MyTemperature(float temperature) { this.m_Temp = temperature; } public string ToString(string format, IFormatProvider formatProvider) { if (String.IsNullOrEmpty(format)) format = "C"; switch (format) { case "F": return (((m_Temp * 9) / 5) + 32) + " °F"; case "C": return m_Temp.ToString() + " °C"; default: throw new FormatException("Invalid format string"); } } } private void button11_Click(object sender, EventArgs e) { MyTemperature temp = new MyTemperature(32.6f); Console.WriteLine(temp.ToString()); //PracticeMCTS.Chapter01.Lesson4+MyTemperature Console.WriteLine(temp.ToString("C", null)); //32.6 °C Console.WriteLine(temp.ToString("F", null)); //90.68 °F }
沒有留言:
張貼留言