最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

C# 反射與特性:EMIT 構(gòu)建代碼

2020-07-13 10:45 作者:微軟MVP-Eleven  | 我要投稿


本期我們將學(xué)習(xí) .NET Core 中,關(guān)于動(dòng)態(tài)構(gòu)建代碼的知識(shí)。其中表達(dá)式樹(shù)已經(jīng)在另一個(gè)系列寫(xiě)了,所以本系列主要是講述 反射,Emit ,AOP 等內(nèi)容。

如果現(xiàn)在總結(jié)一下,反射,與哪些數(shù)據(jù)結(jié)構(gòu)相關(guān)?

我們可以從 AttributeTargets 枚舉中窺見(jiàn):

分別是程序集、模塊、類(lèi)、結(jié)構(gòu)體、枚舉、構(gòu)造函數(shù)、方法、屬性、字段、事件、接口、參數(shù)、委托、返回值。

以往的文章中,已經(jīng)對(duì)這些進(jìn)行了很詳細(xì)的講解,我們可以中反射中獲得各種各樣的信息。當(dāng)然,我們也可以通過(guò)動(dòng)態(tài)代碼,生成以上數(shù)據(jù)結(jié)構(gòu)。

動(dòng)態(tài)代碼的其中一種方式是表達(dá)式樹(shù),我們還可以使用 Emit 技術(shù)、Roslyn 技術(shù)來(lái)編寫(xiě);相關(guān)的框架有 Natasha、CS-Script 等。



01PART構(gòu)建代碼


首先我們引入一個(gè)命名空間:

using System.Reflection.Emit;

Emit 命名空間中里面有很多用于構(gòu)建動(dòng)態(tài)代碼的類(lèi)型,例如?AssemblyBuilder,這個(gè)類(lèi)型用于構(gòu)建程序集。類(lèi)推,構(gòu)建其它數(shù)據(jù)結(jié)構(gòu)例如方法屬性,則有?MethodBuilder、PropertyBuilder?。

程序集(Assembly)


AssemblyBuilder 類(lèi)型定義并表示動(dòng)態(tài)程序集,它是一個(gè)密封類(lèi),其定義如下:

public sealed class AssemblyBuilder : Assembly

AssemblyBuilderAccess 定義動(dòng)態(tài)程序集的訪問(wèn)模式,在 .NET Core 中,只有兩個(gè)枚舉:


另外,程序集的構(gòu)建方式(API)也做了變更,如果你百度看到文章AppDomain.CurrentDomain.DefineDynamicAssembly,那么你可以關(guān)閉創(chuàng)建了,說(shuō)明里面的很多代碼根本無(wú)法在 .NET Core 下跑。

好了,不再贅述,我們來(lái)看看創(chuàng)建一個(gè)程序集的代碼:

AssemblyName assemblyName = new AssemblyName("MyTest"); AssemblyBuilder
assBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Run);

構(gòu)建程序集,分為兩部分:

  • AssemblyName 完整描述程序集的唯一標(biāo)識(shí)。

  • AssemblyBuilder 構(gòu)建程序集

一個(gè)完整的程序集,有很多信息的,版本、作者、構(gòu)建時(shí)間、Token 等,這些可以使用

AssemblyName 來(lái)設(shè)置。

一般一個(gè)程序集需要包含以下內(nèi)容:

  • 簡(jiǎn)單名稱(chēng)。

  • 版本號(hào)。

  • 加密密鑰對(duì)。

  • 支持的區(qū)域性。


你可以參考以下示例:

最終程序集的 AssemblyName 顯示名稱(chēng)是以下格式的字符串:

Name <,culture cultureinfo=""> <,Version = Major.Minor.Build.Revi
sion> <, strongname=""> <,publickeytoken> '\0'

例如:

ExampleAssembly, Version=1.0.0.0, Culture=en, PublicKeyToken=a5d
015c7d5a0b012

另外,創(chuàng)建程序集構(gòu)建器使用?AssemblyBuilder.DefineDynamicAssembly()?而不是?new AssemblyBuilder()?。

模塊(Module)


程序集和模塊之間的區(qū)別可以參考

模塊是程序集內(nèi)代碼的邏輯集合,每個(gè)模塊可以使用不同的語(yǔ)言編寫(xiě),大多數(shù)情況下,一個(gè)程序集包含一個(gè)模塊。程序集包括了代碼、版本信息、元數(shù)據(jù)等。

MSDN指出:“模塊是沒(méi)有 Assembly 清單的 Microsoft 中間語(yǔ)言(MSIL)文件?!薄?/p>

這些就不再扯淡了。

創(chuàng)建完程序集后,我們繼續(xù)來(lái)創(chuàng)建模塊。

類(lèi)型(Type)


目前步驟:

Assembly -> Module -> Type 或 Enum

ModuleBuilder 中有個(gè)?DefineType?方法用于創(chuàng)建?class?和?struct;DefineEnum方法用于創(chuàng)建?enum

這里我們分別說(shuō)明。

創(chuàng)建類(lèi)或結(jié)構(gòu)體:

TypeBuilder typeBuilder = moduleBuilder.DefineType("MyTest.MyCla
ss",TypeAttributes.Public);

定義的時(shí)候,注意名稱(chēng)是完整的路徑名稱(chēng),即命名空間+類(lèi)型名稱(chēng)。

我們可以先通過(guò)反射,獲取已經(jīng)構(gòu)建的代碼信息:

結(jié)果:

接下來(lái)將創(chuàng)建一個(gè)枚舉類(lèi)型,并且生成枚舉。

我們要?jiǎng)?chuàng)建一個(gè)這樣的枚舉:

namespace MyTest { public enum MyEnum { Top = 1, Bottom = 2, Lef
t = 4, Right = 8, All = 16 } }

使用 Emit 的創(chuàng)建過(guò)程如下:

EnumBuilder enumBuilder = moduleBuilder.DefineEnum("MyTest.MyEn
um", TypeAttributes.Public, typeof(int));

TypeAttributes 有很多枚舉,這里只需要知道聲明這個(gè)枚舉類(lèi)型為 公開(kāi)的(Public);typeof(int)?是設(shè)置枚舉數(shù)值基礎(chǔ)類(lèi)型。

然后 EnumBuilder 使用?DefineLiteral?方法來(lái)創(chuàng)建枚舉。

代碼如下:

我們可以使用反射將創(chuàng)建的枚舉打印出來(lái):

Main 方法中調(diào)用:

WriteEnum(enumBuilder.CreateTypeInfo());

接下來(lái),類(lèi)型創(chuàng)建成員,就復(fù)雜得多了。

DynamicMethod定義方法與添加IL


下面我們來(lái)為 類(lèi)型創(chuàng)建一個(gè)方法,并通過(guò) Emit 向程序集中動(dòng)態(tài)添加 IL。這里并不是使用 MethodBuider,而是使用 DynamicMethod。

在開(kāi)始之前,請(qǐng)自行安裝反編譯工具 dnSpy 或者其它工具,因?yàn)檫@里涉及到 IL 代碼。

這里我們先忽略前面編寫(xiě)的代碼,清空 Main 方法。

我們創(chuàng)建一個(gè)類(lèi)型:

public class MyClass{}

這個(gè)類(lèi)型什么都沒(méi)有。

然后使用 Emit 動(dòng)態(tài)創(chuàng)建一個(gè) 方法,并且附加到 MyClass 類(lèi)型中:

運(yùn)行后會(huì)打印字符串。

DynamicMethod 類(lèi)型用于構(gòu)建方法,定義并表示可以編譯、執(zhí)行和丟棄的一種動(dòng)態(tài)方法。 丟棄的方法可用于垃圾回收。。

ILGenerator 是 IL 代碼生成器。

EmitWriteLine 作用是打印字符串,

OpCodes.Ret 標(biāo)記 結(jié)束方法的執(zhí)行,

Invoke 將方法轉(zhuǎn)為委托執(zhí)行。

上面的示例比較簡(jiǎn)單,請(qǐng)認(rèn)真記一下。

下面,我們要使用 Emit 生成一個(gè)這樣的方法:

看起來(lái)很簡(jiǎn)單的代碼,要用 IL 來(lái)寫(xiě),就變得復(fù)雜了。

ILGenerator 正是使用 C# 代碼的形式去寫(xiě) IL,但是所有過(guò)程都必須按照 IL 的步驟去寫(xiě)。

其中最重要的,便是 OpCodes 枚舉了,OpCodes 有幾十個(gè)枚舉,代表了 IL 的所有操作功能。

如果你點(diǎn)擊上面的鏈接查看 OpCodes 的枚舉,你可以看到,很多 功能碼,這么多功能碼是記不住的。我們現(xiàn)在剛開(kāi)始學(xué)習(xí) Emit,這樣就會(huì)難上加難。

所以,我們要先下載能夠查看 IL 代碼的工具,方便我們探索和調(diào)整寫(xiě)法。

我們看看此方法生成的 IL 代碼:

看不懂完全沒(méi)關(guān)系,因?yàn)楣P者也看不懂。

目前我們已經(jīng)獲得了上面兩大部分的信息,接下來(lái)我們使用?DynamicMethod?來(lái)動(dòng)態(tài)編寫(xiě)方法。

定義 Add 方法并獲取 IL 生成工具:

DynamicMethod 用于定義一個(gè)方法;ILGenerator是 IL 生成器。當(dāng)然也可以將此方法附加到一個(gè)類(lèi)型中,完整代碼示例如下:

實(shí)際以上代碼與我們反編譯出來(lái)的 IL 編寫(xiě)有所差異,具體俺也不知道為啥,在群里問(wèn)了調(diào)試了,注釋掉那么幾行代碼,才通過(guò)的。


C# 反射與特性:EMIT 構(gòu)建代碼的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
南岸区| 木里| 加查县| 定州市| 娄底市| 肃北| 岫岩| 达尔| 循化| 镇赉县| 宽城| 青阳县| 手游| 滨海县| 图们市| 子洲县| 郎溪县| 疏附县| 西青区| 枞阳县| 明星| 溧阳市| 富平县| 玉龙| 大埔县| 神农架林区| 会昌县| 双桥区| 栾川县| 巴东县| 依兰县| 商洛市| 榆中县| 隆回县| 寿光市| 无为县| 青神县| 原阳县| 井冈山市| 芒康县| 长泰县|