既然說到匿名型別超級實用,得要找到場景來說服一下,如果大家玩過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的指令量,
可以說編譯器還是非常智慧的,能夠將資源優化到最佳。
好了,大體原理就這樣了,如果你夠聰明,一定會找到適合它的專案場景的 ^_^。