1. 程式人生 > >C#中如何利用操作符過載和轉換操作符 (轉載)

C#中如何利用操作符過載和轉換操作符 (轉載)

操作符過載


有的程式語言允許一個型別定義操作符應該如何操作型別的例項,比如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#的隱式轉換過載很多時候會自動作用於其它運算子過載,下面例子中我們在Program類的Main方法中,使用rational型別的物件rational,來加上int型別的變數number:

//有理數
public 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)
    {
        Console.WriteLine("rational operator + invoked!");

        rational result = new rational(num1.Value + num2.Value);
        return result;
    }

    public static implicit operator rational(int value)
    {
        Console.WriteLine("rational implicit operator invoked!");

        return new rational(value);
    }
}

class Program
{
    static void Main(string[] args)
    {
        int number = 80;
        rational rational = new rational(10);
        rational rationalAdd = rational + number;//rational型別加上int型別

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey();
    }
}

結果如下:

從輸出結果,我們可以看到由於rational型別的物件rational加上int型別的變數number,C#首先呼叫了rational類的implicit operator隱式轉換過載方法,將int型別隱式轉換為了rational型別,然後才呼叫rational類的operator +操作符過載方法,來做的加法。

 

 

 

 

 

 

 

 

原文連結