C# Nut Shell 第十九章 反射和元數據

反射和激活類型

獲取類型

1.GetType()和typeof都可以獲取類型,GetType是在運行時,而typeof是在編譯時

2.可以在獲取一個基礎類型後再構造其他類型

            Type simpleArray = typeof(int).MakeArrayType();
            Console.WriteLine(simpleArray == typeof(int[]));

先獲取int,再獲取int類型的數組和直接獲取int[]類型是一樣的。

3.GetElementType可以返回數組元素的類型

4.獲取嵌套類型GetNestedTypes()

5.類型具有Namespace,Name,Fullname。一般Fullname=Name+Namespace

6.泛型類型帶有'後綴,數字表示有幾個參數

7.Type具有BaseType屬性,可以查找基類

8.GetInterfaces方法會返回類型實現的接口

9.和is等價的動態運算符 IsInstanceOfType,IsAssignableFrom

10.從類型創建實例:

            int i = (int)Activator.CreateInstance(typeof(int));

            ConstructorInfo ci = typeof(X).GetConstructor(new[] { typeof(string) });
            object foo = ci.Invoke(new object[] { null });

11.Type可以表示泛型類型,也可以表示封閉類型,MakeGenericType可以將泛型轉爲封閉類型。GetGenericTypeDefinition可以實現翻轉。


反射並調用成員

1.GetMembers可以返回類型的成員

2.每一個屬性、字段和方法的類型的MemberInfo

3.GetMembers會返回所有類型的成員,可以傳參MemberTypes枚舉來限定返回的成員類型

4.還有專門的返回固定類型的方法,GetMethods,GetFields。。。

成員類型

1.MemberInfo是所有類型信息的抽象基類

2.不同的類型可以由不同的MemberInfo對應的子類去接受

3.*Info實例都會在第一次使用時由反射API緩存,增加性能

4.一個屬性有get和set方法,可以在獲取屬性後獲取它的get和set方法

5.獲取屬性後就可以實現動態綁定了

            object s = "Hello";
            PropertyInfo prop = s.GetType().GetProperty("Length");
            int length = (int)prop.GetValue(s, null);

6.方法的調用

            Type type = typeof(string);
            Type[] par = { typeof(int),typeof(int) };
            MethodInfo method = type.GetMethod("Substring", par);

            object[] arguement = { 2,1 };
            object returnValue = method.Invoke("stamp", arguement);
            Console.WriteLine(returnValue);

對於方法重載,要注意參數的設置

7.檢索調用泛型方法,需要遍歷所有泛型方法,找到符合的那個方法再調用。用表達式樹則可以省略很多

8.動態調用爲幾微秒,用實例委託來代替可以降至幾納秒。

            MethodInfo m = typeof(string).GetMethod("Trim", new Type[0]);
            var trim = (StringToString)Delegate.CreateDelegate(typeof(StringToString), m);

原理就是讓反射只發生一次

9.BindingFlags可以改變枚舉,返回非公有成員

10.使用泛型方法時先要給泛型確定類型

            MethodInfo m = typeof(Program).GetMethod("Echo");
            MethodInfo m2 = m.MakeGenericMethod(typeof(int));
            Console.WriteLine(m2.Invoke(null,new object[] { 3}));

11.調用未知類型的泛型接口成員

通過多次判斷類型(用is),來對不同的類型做出不同的特殊處理


反射程序集

1.返回命名空間下的類型

Type t = Assembly.GetExecutingAssembly().GetType("Net.Program");

2.從類型獲得程序集

 var a = typeof(Test).GetType().Assembly.GetType();

3.加載一個程序集

Assembly a = Assembly.LoadFrom(@"E:\obj\Debug\Framework.exe");

4.由於可以獲得一個程序集的所有類型,所以可以將一些類型專門構成一個程序集。像Asp.Net Core中的Service和其實現類就可以用這種方法


使用特性

CLR允許使用特性將額外的元數據追加到類型、成員和程序集上

特性基礎

1.C#三類特性:位映射特性、自定義特性、僞自定義特性

2.位映射特性:public、abstract。。。

3.自定義特性:自己定義的特性

4.僞自定義特性:系統帶的特性,會由內部轉爲位映射特性

AttributeUsage特性

1.AttributeUsage可以定義特性的特性。

2.AllowMultiple:能否在目標上運用多次

3.Inherited:能否被子類繼承

4.ValidOn:目標集合

定義自定義特性

    public class Person
    {
        [Custom1(Num =4,Name ="hi")]
        public string Name { get; set; }
        private int Age;
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class Custom1Attribute : Attribute
    {
        public int Num { get; set; }
        public string Name { get; set; }
    }

AttributeUsage限定屬性,特性可以給屬性傳參,也可以用構造函數來傳參

在運行時檢索特性

自定義特性用CustomAttribute,GetCustomAttribute來獲取


動態生成代碼

1.DynamicMethod可以在運行時生成方法

2.OpCodes對每一個IL操作碼都有一個對應的靜態只讀字段

            MethodInfo c = typeof(Program).GetMethod("HelloWorld", BindingFlags.Static | BindingFlags.NonPublic);

            b.Emit(OpCodes.Call,c);
            b.Emit(OpCodes.Ret);
            a.Invoke(null, null);

3.評估棧就是參數的臨時存放區

4.將整數加載至評估棧

            MethodInfo c = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)});

            b.Emit(OpCodes.Ldc_I4,123);
            b.Emit(OpCodes.Call, c);
            b.Emit(OpCodes.Ret);

5.Add將一個數字彈出,計算後在壓入

            MethodInfo c = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)});

            b.Emit(OpCodes.Ldc_I4,123);
            b.Emit(OpCodes.Ldc_I4, 123);
            b.Emit(OpCodes.Add);
            b.Emit(OpCodes.Call, c);
            b.Emit(OpCodes.Ret);

6.向動態方法傳參

            var a = new DynamicMethod("Foo", typeof(int), 
new Type[] { typeof(int),typeof(int)}, typeof(Program));
            var b = a.GetILGenerator();
            b.Emit(OpCodes.Ldarg_0);
            b.Emit(OpCodes.Ldarg_1);
            b.Emit(OpCodes.Add);
            b.Emit(OpCodes.Ret);
            int result = (int)a.Invoke(null, new object[] { 3,4});
            Console.WriteLine(result);

7.生成局部變量

ILGenerator.DeclareLocal

            var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
            var gen = dynMeth.GetILGenerator();
            LocalBuilder localX = gen.DeclareLocal(typeof(int));
            LocalBuilder localY = gen.DeclareLocal(typeof(int));
            gen.Emit(OpCodes.Ldc_I4, 6);    //將6壓入棧
            gen.Emit(OpCodes.Stloc, localX);    //將棧中的值給localX
            gen.Emit(OpCodes.Ldc_I4, 7);    //將7壓入棧
            gen.Emit(OpCodes.Stloc, localY);    //將棧中的值給localY
            gen.Emit(OpCodes.Ldloc, localX);    //將localX壓入棧
            gen.Emit(OpCodes.Ldloc, localY);    //將localY壓入棧
            gen.Emit(OpCodes.Mul);      //計算乘積
            gen.Emit(OpCodes.Stloc, localX);    //將乘積的值傳給localX
            gen.EmitWriteLine(localX);
            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

8.分支

ILGenerator.DefineLabel

ILGenerator.MarkLabel

Br(無分支條件),Brture(true則分支),Blt(第一個值小於第二個值則分支)

            var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
            var gen = dynMeth.GetILGenerator();
            Label startLoop = gen.DefineLabel();
            Label endLoop = gen.DefineLabel();

            LocalBuilder x = gen.DeclareLocal(typeof(int));
            gen.Emit(OpCodes.Ldc_I4, 6);    //將6壓入棧
            gen.Emit(OpCodes.Stloc, x);    //將棧中的值給X

            gen.MarkLabel(startLoop);   //開啓一個分支
            gen.Emit(OpCodes.Ldc_I4, 10);
            gen.Emit(OpCodes.Ldloc, x);
            gen.Emit(OpCodes.Blt, endLoop); //跳到結束標記
            gen.EmitWriteLine(x);
            gen.Emit(OpCodes.Ldloc, x);
            gen.Emit(OpCodes.Ldc_I4, 1);
            gen.Emit(OpCodes.Add);
            gen.Emit(OpCodes.Stloc, x);
            gen.Emit(OpCodes.Br, startLoop);
            gen.MarkLabel(endLoop); //分支結束標記
            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

9.實例化對象與調用實例方法

ILGenerator.Newobj

OpCodes.Call:調用靜態方法和值類型的實例方法

OpCodes.Callvirt:調用引用類型的實例方法

            var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
            var gen = dynMeth.GetILGenerator();
            ConstructorInfo ci = typeof(StringBuilder).GetConstructor(new Type[0]);
            gen.Emit(OpCodes.Newobj, ci);
            gen.Emit(OpCodes.Callvirt, typeof(StringBuilder).GetProperty("MaxCapacity").GetGetMethod());
            gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new[] { typeof(int) }));

            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);
            var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
            var gen = dynMeth.GetILGenerator();
            ConstructorInfo ci = typeof(StringBuilder).GetConstructor(new[] { typeof(string),typeof(int)});

            gen.Emit(OpCodes.Ldstr, "Hello");
            gen.Emit(OpCodes.Ldc_I4, 1000);
            gen.Emit(OpCodes.Newobj, ci);

            Type[] strT = { typeof(string) };
            gen.Emit(OpCodes.Ldstr, ", world");
            gen.Emit(OpCodes.Call, typeof(StringBuilder).GetMethod("Append", strT));
            gen.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString"));
            gen.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", strT));

            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

10.異常處理

ILGenerator.BeginExceptionBlock

ILGenerator.BeginCatchBlock

ILGenerator.BeginFinalBlock

ILGenerator.EndExceptionBlock

            var dynMeth = new DynamicMethod("Test", null, null, typeof(void));
            var gen = dynMeth.GetILGenerator();
            MethodInfo emi = typeof(NotSupportedException).GetProperty("Message").GetGetMethod();
            MethodInfo cmi = typeof(Console).GetMethod("WriteLine",new [] { typeof(object)});

            gen.BeginExceptionBlock();
            ConstructorInfo ci = typeof(NotSupportedException).GetConstructor(new Type[0]);
            gen.Emit(OpCodes.Newobj, ci);
            gen.Emit(OpCodes.Throw);
            gen.BeginCatchBlock(typeof(NotSupportedException));
            gen.Emit(OpCodes.Callvirt, emi);
            gen.Emit(OpCodes.Call, cmi);
            gen.BeginFinallyBlock();
            gen.EmitWriteLine("Finally");
            gen.EndExceptionBlock();
            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

生成程序集和類型

保存生成的程序集

有些方法.Net Framework和.Net Core是不一樣的

            AppDomain domain = AppDomain.CurrentDomain;
            AssemblyName name = new AssemblyName("MyEmissions");
            name.Version = new Version(2, 13, 0, 1);
            AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(name, 
AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule", 
"MyEmissions.dll");

            TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);
            MethodBuilder methodBuilder = tb.DefineMethod("SayHello", MethodAttributes.Public, null, null);
            ILGenerator gen = methodBuilder.GetILGenerator();
            gen.EmitWriteLine("Hello World!");
            gen.Emit(OpCodes.Ret);
            tb.CreateType();    //必須要加

            assemblyBuilder.Save("MyEmissions.dll");

2.在CreateType之前,類型屬於未創建狀態

3.動態生成的程序集可以保存成爲文件,該文件可以是dll或者exe,一旦保存成文件後,就可以以靜態的方式調用


生成類型成員

1.生成方法

            MethodBuilder mb = tb.DefineMethod("SquareRoot", 
MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard, typeof(double), new[] { typeof(double) });
            ILGenerator gen2 = mb.GetILGenerator();
            gen2.Emit(OpCodes.Ldarg_0);
            gen2.Emit(OpCodes.Call,typeof(Math).GetMethod("Sqrt"));
            gen2.Emit(OpCodes.Ret);

            Type realType =  tb.CreateType();  
            double x = (double)tb.GetMethod("SquareRoot").Invoke(null, new object[] { 10.0 });
            Console.WriteLine(x);

2.生成字段和屬性

            TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);

            FieldBuilder field = tb.DefineField("length", typeof(int), FieldAttributes.Private);
            FieldBuilder field1 = tb.DefineField("_text", typeof(string), FieldAttributes.Private);
            PropertyBuilder pb = tb.DefineProperty("Text", PropertyAttributes.None, typeof(string), new Type[0]);

            MethodBuilder getter = tb.DefineMethod("get_text", MethodAttributes.Public | MethodAttributes.SpecialName,
     typeof(string), new Type[0]);
            ILGenerator gen = getter.GetILGenerator();
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldfld, field);
            gen.Emit(OpCodes.Ret);

            MethodBuilder setter = tb.DefineMethod("set_text", MethodAttributes.Assembly | MethodAttributes.SpecialName,
     null, new[] { typeof(string)});
            ILGenerator gen2 = setter.GetILGenerator();
            gen2.Emit(OpCodes.Ldarg_0);
            gen2.Emit(OpCodes.Ldarg_1);
            gen2.Emit(OpCodes.Stfld, field);
            gen2.Emit(OpCodes.Ret);

            pb.SetGetMethod(getter);
            pb.SetSetMethod(setter);

            Type t = tb.CreateType();
            object o = Activator.CreateInstance(t);
            t.GetProperty("Text").SetValue(o, "Good", new object[0]);
            string text = (string)t.GetProperty("Text").GetValue(o, null);
            Console.WriteLine(text);

3.生成構造器

            TypeBuilder tb = moduleBuilder.DefineType("Widget", TypeAttributes.Public);

            FieldBuilder field = tb.DefineField("_capacity", typeof(int), FieldAttributes.Private);
            ConstructorBuilder c = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard
                , new Type[0]);

            ILGenerator gen = c.GetILGenerator();
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldc_I4,4000);
            gen.Emit(OpCodes.Stfld, field);
            gen.Emit(OpCodes.Ret);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章