關於Emit的博客已經進入第四篇,在讀本篇博文以前,我但願讀者能先仔細回顧博主以前所編寫的關於Emit的博文,從該篇博文開始,咱們就能夠真正的使用Emit,並把知識轉化爲實戰,我也會把以前的博文連接放在下方,以方便讀者閱讀,你們也能夠將本身的疑問或者指正寫在評論當中,博主會積極進行回覆。html
ok,今天咱們繼續來探索C#-Emit中關於類的知識和應用,今天咱們要來探索和挖掘關於C#屬性的二三事,而且咱們要開始使用Emit中關於類、字段和屬性開啓咱們的第一個應用-動態建立匿名類 java
屬性-C#中讓人既愛又恨的東西,愛的是C#當中由於有了屬性,.NET開發者只須要一句話就能夠完成對類的封裝,根本不須要像其它語言寫這麼多東西,咱們能夠用java來比較一下git
在C#當中咱們定義一個實體屬性程序員
public string Title { get; set; }
在Java當中咱們就須要這樣定義github
private String title; public String getTitle() { return title; } public void setTitle(String value) { title = value; }
在C#當中簡簡單單的一句話在Java當中就須要寫一個字段將兩個方法,固然我不是在貶低Java,只是代表Java沒有在語法上爲開發者提供便利,固然這些年Java的語法也在逐漸完善,從Java8開始逐漸加入了推斷類型var/匿名委託等等優秀的語法。app
扯的有點遠了,固然C#中使用屬性也有它的問題,首先是許多入門級的程序員把屬性當成字段進行氾濫的使用,形成了C#類失去了封裝性,沒有了封裝,有可能就會形成致命的漏洞,因此請剛入門的程序員請慎重使用屬性,屬性雖然好可是不要濫用,在你對屬性不熟悉的時候,尤爲要處理好它的set訪問器,或者拋棄屬性使用如下最原始的方法進行編寫。ide
private string title; public string GetTitle() { return title; } public void SetTitle(string value) { title = value; }
ok,其實在上面與Java的比較當中咱們其實已經知道了屬性是什麼了,屬性是對類中一類特殊方法的語法糖,這一類方法的功能是負責對字段的讀取和設置,稱之爲get/set訪問器,get方法用於獲取字段的值,而set方法是對傳入的值對字段進行賦值,固然,如何賦值和取值,就取決於你方法怎麼寫了。工具
那麼有的讀者就會有疑問,既然屬性只是對於get/set訪問器的語法糖,那麼對應的字段跑哪裏去了呢,其實這裏面還運用了一種叫作自動屬性的語法糖,這是在C#5.0以後增長的一種語法糖,對它詳細的講解能夠查看個人博文《.NET高級特性-Emit(2.1)字段》,文章中詳細說明了C#如何將最終的字段省略的全過程。ui
簡單講完了屬性是什麼以及屬性的本質,咱們就要來簡要說說IL中的屬性,由於Emit當中最終編寫的仍是IL代碼。在IL當中,屬性或者自動屬性它的本來面貌就會被還原,下面的樣例看的就清清楚楚。this
首先,咱們先定義一個Blog類,裏面包含兩個屬性-Title和Content,表示標題和內容
public class Blog { public string Title { get; set; } public string Content { get; set; } }
接着,使用ildasm工具查看IL代碼,ildasm工具博主有在《.NET高級特性-Emit(1)》中講到如何使用,咱們能夠看到僅僅一句話定義Title屬性的話,C#爲我生成四個東西,分別是
咱們雙擊查看Title屬性,能夠看到它的get和set直接連接向get_Title方法和set_Title方法
以後,咱們來觀察下get_TItle方法和set_Title方法,結合上一章《.NET高級特性-Emit(2.1)字段》對字段操做,咱們很明顯的看到,set_Title方法實現了對字段的賦值,而get_Title方法也正好對應了字段的取值
這就是在IL中呈現的屬性的真正樣貌,有了IL的理解,咱們就能開始咱們的Emit之旅了。
屬性的定義其實很簡單,屬性真正的難點是在於如何編寫get/set訪問器,由於這纔是屬性的核心邏輯,並且對於自動屬性來講咱們須要定義字段/get訪問器/set訪問器和屬性自己,因此博主打算用一個方法來實現自動屬性的生成,已完成這一個過程的複用
首先,咱們來看一下方法定義,博主使用了擴展方法來爲TypeBuilder擴展一個定義自動屬性的方法,該方法只須要屬性名稱和類型,便可建立自動屬性須要定義的字段/get訪問器/set訪問器和屬性自己,工欲善其事必先利其器,有了這個方法咱們就能快速的建立自動屬性
public static PropertyBuilder DefineAutomaticProperty(this TypeBuilder typeBuilder, string propertyName, Type propertyType) { //do something }
(1)而後,咱們定義屬性的字段,因爲是自動屬性,因此字段的類型與屬性類型相同,名稱博主採用下劃線+屬性小寫的方式定義
var fieldBuilder = typeBuilder.DefineField("_" + propertyName.ToLower(), propertyType, FieldAttributes.Private);
(2)以後,咱們定義屬性,這個時候的屬性是沒有任何get/set訪問器的
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, Type.EmptyTypes);
(3)在定義完屬性以後,咱們開始編寫屬性的get方法,get方法的內容是讀取字段值並返回,如何編寫能夠參考個人文章《.NET高級特性-Emit(2.1)字段》中字段操做一節
//定義Get方法,返回屬性類型,入參無 var getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, propertyType, Type.EmptyTypes); var getIL = getMethodBuilder.GetILGenerator(); getIL.Emit(OpCodes.Ldarg_0); //將字段放入棧頂 getIL.Emit(OpCodes.Ldfld, fieldBuilder); getIL.Emit(OpCodes.Ret);
(4)以後,咱們一樣定義屬性的set方法,內容爲讀取第一個參數並保存到字段,emit含義一樣能夠參考上一步get方法的文章
//定義Set方法,返回void,入參一個,類型爲屬性類型 var setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, null, new Type[] { propertyType }); var setIL = setMethodBuilder.GetILGenerator(); setIL.Emit(OpCodes.Ldarg_0); //將第一個參數放入棧頂 setIL.Emit(OpCodes.Ldarg_1); //將棧頂元素彈出並保存到字段 setIL.Emit(OpCodes.Stfld, fieldBuilder); setIL.Emit(OpCodes.Ret);
(5)最後,咱們將get和set方法設置爲屬性的get和set
propertyBuilder.SetGetMethod(getMethodBuilder); propertyBuilder.SetSetMethod(setMethodBuilder);
(6)返回屬性,咱們的定義自動屬性方法就完成了,完整代碼用戶能夠查看個人github:
return propertyBuilder;
在定義自動屬性方法中,咱們定義了字段/屬性/get訪問器與set訪問器,這樣,咱們定義Blog類就很是的簡單
首先,咱們只要先定義Blog類
var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Edwin.Blog.Emit"), AssemblyBuilderAccess.Run); var moduleBuilder = asmBuilder.DefineDynamicModule("Edwin.Blog.Emit"); var typeBuilder = moduleBuilder.DefineType("Blog", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.BeforeFieldInit);
而後直接使用上面咱們定義的擴展方法來定義自動屬性
typeBuilder.DefineAutomaticProperty("Title", typeof(string)); typeBuilder.DefineAutomaticProperty("Content", typeof(string));
最後建立類型,就完成了咱們對Blog類的建立
typeBuilder.CreateTypeInfo().AsType();
最後建立並對屬性賦值
dynamic user = Activator.CreateInstance(type); user.Title = "Emit高級特性-屬性"; user.Content = "xxx";
便可在調試窗口看到以下結果
樣例github地址:https://github.com/MJEdwin/edwin-blog-sample/blob/master/Edwin.Blog.Sample/Property/BlogEmit.cs
請讀者思考,若是我定義一個方法,方法中傳入類所須要的屬性的名稱和它對應的類型,咱們是否是就能夠根據上述建立Blog的方式來建立一個只包含屬性和字段的類,這樣的類不就是咱們C#當中所說的匿名類了嗎?
想一想,咱們日常開發當中何時使用匿名類居多?博主告訴你,沒錯就是Mapper,C#當中匿名類存在的意義就是能夠實現實體對象到匿名對象的映射,使用最普遍的就是在Linq當中,那麼若是咱們用Emit來建立匿名類,再在Linq中完成實體類到匿名類的映射,咱們我就能夠動態DynamicLinq了嗎?
因爲篇幅緣由以及其中包含了表達式樹的緣由,故博主就不將代碼放在博文當中,有興趣的讀者能夠查看個人github瞭解實現,博主將動態Select的流程圖畫在下方,知識豐富的小夥伴也能夠自行實現。
本章講解了屬性是什麼,Emit如何編寫屬性,以及屬性最重要的一個應用-建立匿名類;不積跬步無以致千里,不積小流無以成江海,做爲身處軟件行業的咱們來講,更須要這一份鍥而不捨的積累,只有不斷的積累-思考-積累-思考,才能從量變完成質變,寫出更加優秀的代碼和軟件。
博主將繼續更新.NET高級特性系列,感謝閱讀!!!
原文出處:https://www.cnblogs.com/billming/p/emit-study-property.html