看看這個超級實用的一種型別——匿名型別

一線碼農發表於2014-10-20

  既然說到匿名型別超級實用,得要找到場景來說服一下,如果大家玩過php,裡面有一個萬能的關聯陣列array,任你在關聯陣列array裡面怎麼寫,都

可以用json_encode來生成json,非常非常的方便。

<?php
   //可以這麼寫
   $arr= array("name"=>"hxc","age"=20,"isMale"=>true);

   //也可以這麼寫
   $arrayName = array("list" =>array(
                                           array("name" => "john", "age" => "20","isMale" => true),
                                           array("name" => "mary", "age" => "24","isMale" => false),
                                           array("name" => "jackson", "age" => "30","isMale" => true)
                                        ),"totalCount"=>100);

   $json=json_encode($arr);

   echo $json;

?>

 

而在使用C#的時候,我們要向前臺輸出json的時候,都是要先定義一個實體,再給實體各種賦值,然後序列化成json的形式輸出到前臺,就比如下面這樣:

 

 1     public class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var json = new Student() { Name = "john", Age = 25, IsMale = true };
 6 
 7             JavaScriptSerializer js = new JavaScriptSerializer();
 8 
 9             var r = js.Serialize(json);
10     }

 

自從.netframework 3.5中新增了匿名型別之後,一切都有了新的變化。

 

一:尋找場景

<1>  場景1:

   有時候我們想向前臺輸出json,但是這個json是個非常簡單的狀態json,就像這樣{"status":"1","message":"提交成功"},但是以往的做法我必須要自己

先定義一個狀態類,再序列化它,就像下面這樣。

 1     public class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var json = new Status() { status = "1", message = "提交成功" };
 6 
 7             JavaScriptSerializer js = new JavaScriptSerializer();
 8 
 9             var result = js.Serialize(json);
10 
11             Console.WriteLine(result);
12 
13             Console.Read();
14         }
15 
16         public class Status
17         {
18             public string status { get; set; }
19 
20             public string message { get; set; }
21         }
22     }

 

再看看我們使用匿名型別的話,會是怎樣?

 1         static void Main(string[] args)
 2         {
 3             var json = new { status = "1", message = "提交成功" };
 4 
 5             JavaScriptSerializer js = new JavaScriptSerializer();
 6 
 7             var result = js.Serialize(json);
 8 
 9             Console.WriteLine(result);
10 
11             Console.Read();
12         }

從上下文的程式碼量來說,確實讓我們少寫了很多程式碼,也就提高了我們的開發效率,有了這個匿名型別之後,我們也可以像php的array一樣,隨心所欲的定義

簡單或者複雜的結構,然後序列化為json。

 

<2> 場景2:

    還有一個經常用到的場景就是,我們在獲取列表資料的時候,經常是採用分頁的形式,比如一頁是20條資料,但是為了前端方便分頁,後端必須要傳遞一

個totalcout引數,這樣的話,前端才知道pagecount=Math.ceil(totalcount/pagesize),算出總頁數,在傳統的方法上,我們需要在底層的List上再包裝

一個類,然後再在這個類中增加一個totalcount屬性,就像下面這樣。

 1     /// <summary>
 2     /// 集合包裝類
 3     /// </summary>
 4     public class StudentPage
 5     {
 6         public List<Student> list { get; set; }
 7         public int total { get; set; }
 8     }
 9     /// <summary>
10     /// student實體類
11     /// </summary>
12     public class Student
13     {
14         public string Name { get; set; }
15 
16         public int Age { get; set; }
17 
18         public bool IsMale { get; set; }
19     }

 

有了匿名型別之後,我們再也不用這麼寫了,應該像下面這樣。

 

 1     public class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             var list = new List<Student>()
 6             {
 7                   new Student(){ Name="john", Age=25, IsMale=true},
 8                   new Student(){ Name="mary", Age=24, IsMale=false},
 9                   new Student(){ Name="jackson",Age=30,IsMale=true}
10             };
11 
12             //核心點
13             var json = new { List = list, TotalCount = 20 };
14 
15             JavaScriptSerializer js = new JavaScriptSerializer();
16 
17             var result = js.Serialize(json);
18 
19             Console.WriteLine(result);
20 
21             Console.Read();
22         }
23     }
24 
25     public class Student
26     {
27         public string Name { get; set; }
28 
29         public int Age { get; set; }
30 
31         public bool IsMale { get; set; }
32     }

 

看到這樣的json是不是有一種很爽的感覺?是的,確實在我們開發中非常的實用,那麼問題來了,這麼實用東西,它的原理在哪裡可以學得到?

 

二:基本原理

  既然想學,我們就剖析下它的IL程式碼,看看這個東西到底都做了些什麼?為了方便理解,我就定義一個非常簡單的匿名類。

1 var json = new { Name = "jackson", Age = 20 };

 

然後我們看看IL中到底都發生了什麼?

 

不看IL還好,一看真是嚇一跳,就一句話的事情,變成IL後就有這麼多的玩意。。。而且類名取得也是非常奇葩,開頭居然有<>這種尖括號,當

然這麼寫的原因很簡單,就是避免我們定義的類名與自動生成的相沖突,再說編譯器也不允許用<>開頭的類名,雖然在CLR層面是允許的,好了,

我們繼續往下面,從IL上我們還發現了

兩個模板引數:<Age>j__TPar 和 <Name>j__TPar。

兩個欄位:<Age>i__Field 和<Name>i__Field。

兩個屬性方法:get_Name和get_Age,這裡我們發現並沒有set_Name和set_Age方法,也就說明該屬性是個只讀屬性。

 

最後我們還發現匿名型別還重寫了equals,gethashcode 和 toString 方法,這裡我就只看下equals方法吧。

 

 

可以看到,當型別中有泛型引數的加入,IL程式碼就變得非常難看並且容易混淆,不過可以找到幾個關鍵指令,在重寫object的equals方法之後,

匿名型別中比較相等的方法是採用逐一欄位比較的,這就跟值型別的比較方式很類似了,既然是逐一比較,那麼下面的兩個匿名物件應該是相等的。

這個在引用型別中是不可想象的。

 

不過有趣的是,這時候我們再來看看IL程式碼,發現並沒有生成兩個匿名類,而是json和json2公用一個匿名類,這個好處就是減少了IL的指令量,

可以說編譯器還是非常智慧的,能夠將資源優化到最佳。

 

好了,大體原理就這樣了,如果你夠聰明,一定會找到適合它的專案場景的 ^_^。

 

相關文章