1. 程式人生 > >C# 匿名物件(匿名型別)、var、動態型別 dynamic

C# 匿名物件(匿名型別)、var、動態型別 dynamic

本文是要寫的下篇《C#反射及優化用法》的前奏,不能算是下一篇文章的基礎的基礎吧,有興趣的朋友可以關注一下。

隨著C#的發展,該語音內容不斷豐富,開發變得更加方便快捷,C# 的鋒利盡顯無疑。C# 語言從誕生起就是強型別語音,這一性質到今天不曾改變,我想以後也不會變。既然是強型別語音,那編寫任一程式均要求滿足下面的基本條件:

1、變數宣告必須指明其型別

2、變數型別明確後,其型別在Runtime亦不能改變

程式碼如下:

複製程式碼

    public  class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Like { get; set; }
    }

複製程式碼

複製程式碼

static void Main(string[] args)
{
    int a = 10;
    string s = "abc";
    Student student = new Student();
    //下面出現編譯錯誤,變數型別在聲明後無法再變更
    s = a;
    student = s;
    a = 10.1f;
}

複製程式碼

然而,在實際開發中我們經常面臨如下幾種常見問題:

1、在一個較大的程式程式中,只有一處或很少幾處(不超過3處)需要用到 某個或某些型別(如上 Student),其他地方不再需要這些型別。單獨宣告一個Student型別,所需程式碼量,可能超過使用該型別時的程式碼量,投入產出比不划算。

2、在一處程式中,只需要某種型別物件的部分屬性或方法參與運算。在這種情況下將該型別物件臨時轉換為程式所需的部分屬性和方法的物件,可使程式更加精簡。

3、其他情況........我暫未留意到......歡迎補充........

上面這些 C# 實際開發中常見問題,在 JavaScript 開發中有著比較好的解決方案,如下:

複製程式碼

//在此處js中需要模擬一個學生物件
student = {"name":"張三","age":20,"like":"LOL"};
//在此處js中需要模擬一個老師物件
teacher = {"name":"李老師","like":"沒收學生手機,自己LOL"};
//此處需要將學生student轉換成只有name和age的物件
person = {"name":student.name,"age":student.age};

複製程式碼

如果你不熟悉上面的js語法,你可以去百度搜索 “json語法”,告訴你很簡單哦(而且很重要)。

匿名物件(匿名型別)

因此C#在3.0版本中吸收了JavaScript指令碼語言的這種語法優點,對C#做了相應升級使其也支援這種語法形式(C#依然是強型別語言)。示例程式碼如下:

static void Main(string[] args)
{
     new {Name="張三",Age=20,Like="LOL"};
}

上面的C#程式碼 通過new關鍵字告訴編譯器要建立一個物件,該物件具有Name,Age,Like三個屬性,=後為屬性對應的值。如此我們避開了“建立一個物件首先要有該物件型別的約束”,因此在開發過程中對於使用較少的型別我們無需再建立單獨的類了,上面提到的問題1被解決。

現在創建出來的物件沒指定具體型別,因此稱為匿名物件。

Var登場

現在要使用匿名物件,則需要使用變數引用它。雖然我們在建立時沒有指定物件的型別,但編譯器會在編譯過程中幫我們建立一個具有相關屬性和方法的型別。此時編譯出的型別名稱是隨機生成的,因此變數型別無法確定。示例如下:

複製程式碼

static void Main(string[] args)
{
    //XXX為型別宣告
    //x為引用變數 
     XXX x = new {Name="張三",Age=20,Like="LOL"};
}

複製程式碼

雖然我們不知道編譯器生成的型別名稱,但我們可 讓編譯器自己根據編譯的結果來推斷變數型別。此時var關鍵字便發揮作用了:

 

static void Main(string[] args)
{
     var x = new {Name="張三",Age=20,Like="LOL"};
}

var 關鍵字說明 x 的型別由賦於的值來決定(推定),並能根據編譯器推定給出智慧提示,如下圖:

var使用注意事項:

1、var 僅能宣告方法內的區域性變數

2、var 宣告的變數在被賦值後型別即確定下了,後續程式中不能在賦其他型別的值

3、var x = new object() 沒有意義,不要寫這樣的程式碼...............

 現在有匿名物件和var推斷型別的支援,我們就能處理上面提到的問題2。示例程式碼如下:

        static void Main(string[] args)
        {
            var x = new { Name = "張三", Age = 20, Like = "LOL" };
            var s = new { Name = x.Name, Age = x.Age };  
        }

上面僅為示例,如果你熟悉Linq或Entity Framework,那問題2對應的用法將是鋪天蓋地的.......

動態型別 dynamic 出場

對於匿名型別的使用一般侷限於方法的區域性,可理解為:隨用隨定義,用完就消失。有如下情況應該怎麼辦?

複製程式碼

        static void Main(string[] args)
        {
            var x = GetObject(); 
        }

        private static XXX GetObject()
        {
            return new { Name = "張三", Age = 20, Like = "LOL" };
        }

複製程式碼

通過GetObject方法返回一個匿名物件,所以方法返回值 型別名稱無法確定,此處暫時用XXX代替。在這種情況下返回的型別不確定,可以使用 dynamic 來指明。如下:

複製程式碼

        static void Main(string[] args)
        {
            var x = GetObject(); 
            Console.WriteLine(x.Name);
        }

        private static dynamic GetObject()
        {
            return new { Name = "張三", Age = 20, Like = "LOL" };
        }

複製程式碼

此時方法不會出現語法錯誤,程式可以成功編譯並執行。那麼 dynamic 到底做了什麼,可以使上面的程式成功編譯呢?

dynamic的作用:

1、dynamic 表示動態型別,動態型別的含義就是 程式編寫、編譯階段 型別不確定,在Runtime時再通過反射機制確定相關物件的屬性或方法。因此編寫階段不會進行語法檢測。

2、dynamic 可用來宣告 欄位、屬性、方法引數、方法返回值

3、dynamic 不支援智慧提示,因為你寫程式碼時 dynamic  是什麼沒法知曉(反射)

dynamic 宣告的變數,可理解為 object 型別變數。所以給dynamic變數賦任何型別值都正確,但在使用變數來取得某個屬性值或呼叫某方法時(此時程式肯定處於Runtime狀態),CLR會檢查(反射)所呼叫的屬性或方法是否存在,不存在報執行時異常。

dynamic在 Asp.net Mvc web開發中處處使用,雖然看上去很複雜,本質就上面所說內容。

說明:

var 和 dynamic 看似功能類似,但它們是不同的:

  var dynamic
 宣告欄位  ×  √
 區域性變數  √  √
 方法引數型別  ×  √
 方法返回值型別  ×  √