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);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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