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