C# 對象類型轉換

在日常開發時,經常需要將對象從一種類型轉換爲另一種類型。CLR允許將對象轉換爲它的(實際)類型或者它的任何基類型。

C#不要求任何特殊語法即可將對象轉換爲它的任何基類型,因爲向基類型的轉換被認爲是一種安全的隱式轉換。然而,將對象轉換爲它的某個派生類型時,C#要求開發人員只能進行顯式轉換,因爲這種轉換可能在運行時失敗。可以理解爲:父類強制轉換成子類,子類隱式轉換成父類

    internal class Employee
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
        // 子類隱式轉換成父類,不需要轉換
        // 因爲new返回一個Employee對象,而Object是Employee的基類
        Object o = new Employee();
        // 子類可以自動轉父類 可以這麼理解把子類的實例em的地址賦值給了o1, o1的地址就是em的地址
        // 這時就可以調用Employee 類的方法,點出Employee 類的屬性
        Employee em = new Employee();
        Object o1 = em;

            // 父類強制轉換成子類, 需要轉換
            // 因爲Employee派生自Object
            Employee e = (Employee)o;
        }
    }

在運行時,CLR會檢查轉型操作,確定總是轉換爲對象得實際類型或者它的任意類型。下面的代碼雖然能通過編譯,但會在運行時拋出InvalidCastException異常:

    internal class People
    {
    }

    internal class Employee : People
    {
    }

    internal class Manager : Employee
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 創建實例的時候沒有將父類引用到子類對象,是無法轉換的
            // PromoteEmployee不能運行成功
            People p = new People();
            PromoteEmployee(p);

            // 創建實例的時候將父類引用到子類對象,是可以轉換的
            // PromoteEmployee能運行成功
            People p1 = new Employee();
            PromoteEmployee(p1);

            // Manager"屬於"(IS-A)Employee對象
            // PromoteEmployee能運行成功
            Manager m = new Manager();
            PromoteEmployee(m);

            // DateTime不是從Employee派生的
            // PromoteEmployee不能運行成功
            DateTime newYears = new DateTime(2011, 10, 1);
            PromoteEmployee(newYears);
        }

        static void PromoteEmployee(Object o)
        {
            // 編譯器在編譯時無法準確地獲知對象0引用的是什麼類型,因此允許代碼通過編譯
            // 但在運行時,CLR知道了o引用的是什麼類型(在每次執行轉型的時候)
            // 所以它會覈實對象的類型是不是Employee或者從Employee派生的任何類型
            Employee e = (Employee)o;
        }
    }

使用C#的is和as操作符來轉型
在C#語言中進行轉換的另一種方式是使用is操作符。is檢查對象是否兼容於指定類型,返回Boolean值true或false。注意,is操作符永遠不拋出異常,例如以下代碼:

        static void Main(string[] args)
        {
            Object o = new Object();
            Boolean b1 = (o is Object);    //True
            Boolean b2 = (o is Employee);  //False
            Boolean b3 = (o is Nullable);  //False
        }

is操作符通常像下面這樣使用:

        static void Main(string[] args)
        {
            Object o = new Employee();
            if( o is Employee )
            {
                Employee e = (Employee)o;
            }
        }

在上訴代碼中,CLR實際檢查兩次對象類型。is操作符首先覈實o是否兼容於Employee類型。如果是,在if語句內部轉型時,CLR再次覈實o是否引用一個Employee。CLR的類型檢查增強了安全性,但無疑會對性能造成一定的影響。這是因爲CLR首先必須判斷變量(o)引用的對象的實際類型。然後CLR必須遍歷繼承層次結構,用每個基類型去核對指定的類型(Employee)。由於這是一個相當常用的編程模式,所以C#專門提供了as操作符,目的就是簡化這種代碼的寫法,同時提升其性能。

        static void Main(string[] args)
        {
            Object o = new Employee();
            Employee e = o as Employee;
            if(e != null)
            {

            }
        }

在這段代碼中,CLR覈實o是否兼容於Employee類型;如果是,as放回對同一個對象的非null引用。如果o不兼容於Employee類型,as返回null。as操作符永遠不拋出異常。注意,as操作符造成CLR只校驗一次對象類型。if語句只檢查e是否爲NULL;這個檢查的速度不校驗對象的類型快得多。

測試代碼:

namespace ConsoleApplicationTest
{
    internal class B{}

    internal class D : B{}

    class Program
    {
        static void Main(string[] args)
        {
            Object o1 = new Object(); //OK
            Object o2 = new B(); //OK
            Object o3 = new D(); //OK
            Object o4 = o3; //OK
            B b1 = new B(); //OK
            B b2 = new D(); //OK
            D d1 = new D(); //OK

            B b3 = new Object(); //編譯時錯誤,正確:Object b3 = new B();
            D d2 = new Object(); //編譯時錯誤,正確:Object b3 = new D();

            B b4 = d1; //OK,子類可以自動轉父類
            D d3 = b2; //編譯時錯誤,正確:D d3 = (D)b2;
            D d4 = (D)d1; //OK
            D d5 = (D)b2; //OK

            D d6 = (D)b1; //運行時錯誤,創建實例的時候沒有將父類引用到子類對象
            B b5 = (B)o1; //運行時錯誤,創建實例的時候沒有將父類引用到子類對象
            B b6 = (D)b2; //OK,創建實例的時候將父類引用到子類對象
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章