操作符重載
有的編程語言允許一個類型定義操作符應該如何操作類型的實例,比如string類型和int類型都重載了(==)和(+)等操作符,當編譯器發現兩個int類型的實例使用+操作符的時候,編譯器會生成把兩個整數加到一起的代碼。
當編譯器發現兩個string類型的實例使用+操作符的時候,編譯器會生成把兩個字符串連接到一起的代碼。那么編譯器怎么就會知道這樣做呢?如何進行操作符重載呢?
下面C#代碼展示了一個類中如何進行操作符重載:
namespace DoNet.Seven.ConsoleApplicationTest { class Program { static void Main(string[] args) { rational r1 = new rational(10); rational r2 = new rational(5); rational r3= r1 + r2; Console.WriteLine(r3.Value); Console.ReadKey(); } } //有理數 public sealed class rational { private int _value = 0; public int Value { get { return _value; } set { _value = value; } } public rational(int value) { this._value = value; } public rational() { } public static rational operator+(rational num1,rational num2) { rational result = new rational(num1.Value+num2.Value); return result; } } }
運行代碼輸入結果是15
用IL工具看下編譯器生成的代碼如下:
1、首先CLR規范要求操作符重載方法必須是public和static方法。另外,C#編譯器要求操作符重載方法至少有一個參數的類型與當前定義的這個方法的類型相同。之所以這樣做,
是為了是編譯器能在合理的時間內找到要綁定的操作符方法。
2、編程語言的編譯器看到源碼中出現一個+操作符時,會檢查是否有一個操作數的類型定義了一個名為op_Addtion的specialname方法,而且該方法的參數兼容於操作數的類型,
如果存在這樣的一個方法,編譯器就生成調用它的代碼。如果不存在這樣的一個方法,就生成一個編譯錯誤。
3、對於其它操作符編譯之后對應的方法如下表所示(左邊是一元操作符,右邊是二元操作符)
轉換操作符
當設計一個類型時應該考慮到和其它類型之間的轉換,這個其實很重要,將對我們的編碼有很大的好處,就像每個類型都會有的一個方法Tostring()一樣,我們定義一個int類型,可以很方便的用tostring()方法把
int轉換為string,當然也可以轉換為其它類型。就像上面的rational一樣,如果能將一個int或者double轉換為一個rational,就會很方便,反之亦然。
//有理數 public sealed class rational { private int _value = 0; public int Value { get { return _value; } set { _value = value; } } public rational(int value) { this._value = value; } public rational(double value) { this._value =(int)value; } public rational() { } public int ToInt() { return _value; } public double ToDouble() { return (double)_value; } public static rational operator+(rational num1,rational num2) { rational result = new rational(num1.Value+num2.Value); return result; } }
1、調用這些構造器和方法,開發人員可以很方便的將int和double對象轉換成rational對象,這將給編程工作帶來很多方便。設計類型時,應該認真考慮類型需要支持的轉換構造器和方法。
2、int i=10;long j=i;這樣的代碼我們經常會看到,那么從int類型到long類型的轉換為什么就可以隱士的進行呢?這就涉及到了我們的轉換操作符,下面我們也為rational定義幾個轉換操作符。
namespace DoNet.Seven.ConsoleApplicationTest { class Program { static void Main(string[] args) { int n = 10; rational r1 = n; double d=(double)r1; Console.WriteLine(r1.Value); Console.WriteLine(d.ToString()); Console.ReadKey(); } } //有理數 public sealed class rational { private int _value = 0; public int Value { get { return _value; } set { _value = value; } } public rational(int value) { this._value = value; } public rational(double value) { this._value =(int)value; } public rational() { } public int ToInt() { return _value; } public double ToDouble() { return (double)_value; } public static rational operator+(rational num1,rational num2) { rational result = new rational(num1.Value+num2.Value); return result; } public static implicit operator rational(int value) { return new rational(value); } public static implicit operator rational(double value) { return new rational(value); } public static explicit operator int(rational value) { return value.ToInt(); } public static explicit operator double(rational value) { return value.ToDouble(); } } }
輸出的結果是10、10。 我們可以在rational、int、double之間來回轉換,是不是覺得挺方便的,在這個過程中,編譯器又幫我們做了什么呢?
在C#中,implicit關鍵字告訴編譯器為了生成代碼來調用方法,不需要在源代碼中進行顯示轉換,相反,explicit關鍵字告訴編譯器只有在發現了顯示轉型時,才調用方法。
在implicit或explicit關鍵字之后,要指定operator關鍵字告訴編譯器該方法是一個轉換操作符。在operator之后,指定對象要轉換成什么類型。在圓括號內,則指定要從什么類型轉換。
C#編譯器檢測到代碼中的轉型,並內部生成IL代碼來調用rational類型定義的轉換操作符方法,如果用反編譯器看的話可以發現,轉換操作符方法會生成下面這樣的代碼:
結論
不論是操作符重載還是轉換操作符,都是在設計類型是考慮到我們編碼方便而設計的,下面我們看下C#中decimal類型中的定義。