C# 反射详解一

Posted 用代码编织世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 反射详解一相关的知识,希望对你有一定的参考价值。

首先反射是基于System.Reflection命名空间下,.Net框架提供的帮助类库,可以读取并使用metadata(元数据:描述对象信息的数据).

我们再来看下代码生成编译的总过程。

编译器编译(一次编译):类库生成的都是dll,控制台生成的是exe文件

 dll和exe都包含两大块metadata和IL(中间语言)

dll和exe的运行环境依赖于CLR,我们安装.net frmework时会自动配置CLR和JIT(及时编译器)

JIT(二次编译(编译之后,可以在不同平台使用))会将dll和exe文件转为机器码

反射基础 

三种加载dll程序集的方法

//根据dll名称加载,不带dll后缀Assembly assembly = Assembly.Load("Bussiness");//完整路径加载,注意web层要引用加载的Bussiness层,如果没有依赖项,使用时会报错Assembly assembly1 = Assembly.LoadFile(@"E:反射RelinDebug
etcoreapp3.1Bussiness.dll");//根据dll名称加载,带dll后缀Assembly assembly2 = Assembly.Load("Bussiness.dll");
//获取该dll中的模块信息foreach (var item in assembly.GetModules()){ Console.WriteLine(item.FullyQualifiedName); // => E:反射RelinDebug etcoreapp3.1Bussiness.dll}//获取该dll中的类型信息foreach (var item in assembly.GetTypes()){ Console.WriteLine(item.FullName); // => Bussiness.Class1 // => Bussiness.Method}

这是Bussiness层信息

C# 反射详解一

 根据反射创建对象

//加载程序集Assembly assembly3 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly3.GetType("Common.StringHelper");//创建对象object o = Activator.CreateInstance(type);

Common层内容

 当我们创建对象时,会打印 StringHelper被构造了! 因为每一个类都 自带一个构造函数,间接执行了WriteLine语句。

但是当我们调用QueryString方法时就报错了

 原因是编译器不认可,编译器只把他当成是一个onject对象。

我们可以这样写

//加载程序集Assembly assembly3 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly3.GetType("Common.StringHelper");//创建对象dynamic o = Activator.CreateInstance(type);o.QueryString("今天真热");
dynamic会避开编译器的检查,相当于弱语言类型,从而达到我们的预想.
对此我们可以加以封装一下
public static StringHelper CreateHelper(){ //加载程序集 Assembly assembly3 = Assembly.Load("Common"); //获取指定类型对象的(类型)信息 Type type = assembly3.GetType("Common.StringHelper"); //创建对象 dynamic o = Activator.CreateInstance(type); return o;}//=>调用 /*StringHelper stringHelper = Factory.CreateHelper();    await stringHelper.QueryString("开空调");  */

反射破坏单例:单例类在内存中是唯一的,本来是不能实例化,但是可通过反射来创建对象,从而调用其私有构造函数。

public sealed class Single{ private Single() { Console.WriteLine($"{this.GetType().Name}已被构造"); }  private static Single single = null;
public static Single CreateInstance() { if (single == null) { single = new Single(); } return single; }}
//加载程序集Assembly assembly3 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly3.GetType("Common.Single");//创建对象Common.Single single = (Common.Single)Activator.CreateInstance(type, true);

根据参数调用不同的构造函数

public class TestClass{ public TestClass(string parameter) { Console.WriteLine($"{this.GetType().Name}已被构造"); } public TestClass(int parameter) { Console.WriteLine($"{this.GetType().Name}已被构造"); }}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.TestClass");//创建对象 这里有一个参数数组,会根据参数的顺序和类型匹配对应的构造函数,如果参数是空数组或null,则构造函数不接受任何参数(无参数构造函数)Common.TestClass single = (Common.TestClass)Activator.CreateInstance(type, 1);Common.TestClass single1 = (Common.TestClass)Activator.CreateInstance(type, "1");

反射操作泛型

public class GenericClass<T, R, E>{ public void Show(T t, R r, E e){ Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}"); }}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息 注意:`3是占位符,该泛型对象中有几个泛型参数Type type = assembly4.GetType("Common.GenericClass`3");//不能像这样创建对象,因为泛型对象在实例化的时候要指定参数及类型//object o = Activator.CreateInstance(type);//指定泛型对象的参数类型Type type1 = type.MakeGenericType(new Type[] { typeof(decimal), typeof(Guid), typeof(Enum) });object oGeneric = Activator.CreateInstance(type1);

调用无参方法

public class ReflectionTest{ public void show1() { Console.WriteLine($"调用了{this.GetType()}中的方法show1"); }}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.ReflectionTest");//创建对象object o = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("show1");//调用方法,第一个参数是实例类型,第二个是参数列表methodInfo.Invoke(o, null);

有参方法的调用

public void show2(int i){ Console.WriteLine($"调用了{this.GetType()}中的方法show2");}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.ReflectionTest");//创建对象object o = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("show2");//第一个参数是实例类型,第二个是参数列表methodInfo.Invoke(o, new object[] { 1 });

静态方法的调用

public static void show3(int i){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show3");}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.ReflectionTest");//创建对象object o = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("show3");//第一个参数是实例类型,第二个是参数列表//因为是静态方法,所以不需要传实例对象(可传可不传),静态方法是可以通过方法名直接访问的methodInfo.Invoke(null, new object[] { 1 });

重载方法的调用

public static void show4(){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show4");}
public static void show4(int i){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show4");}
public static void show4(string s){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show4");}public static void show4(string s, int i){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show4");}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.ReflectionTest");//创建对象object o = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("show4", new Type[] { });//第一个参数是实例类型,第二个是参数列表//因为是静态方法,所以不需要传实例对象(可传可不传),静态方法是可以通过方法名直接访问的methodInfo.Invoke(null, new object[] { });{ MethodInfo methodInfo1 = type.GetMethod("show4", new Type[] { typeof(int) }); methodInfo1.Invoke(null, new object[] { 1 });}{ MethodInfo methodInfo2 = type.GetMethod("show4", new Type[] { typeof(string) }); methodInfo2.Invoke(null, new object[] { "1" });}{ MethodInfo methodInfo3 = type.GetMethod("show4", new Type[] { typeof(string), typeof(int) }); methodInfo3.Invoke(null, new object[] { "1", 1 });}
私有方法的调用
private void show5(string s){ Console.WriteLine($"调用了{typeof(ReflectionTest)}中的方法show5");}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息Type type = assembly4.GetType("Common.ReflectionTest");//创建对象object o = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("show5", BindingFlags.Instance | BindingFlags.NonPublic);//第一个参数是实例类型,第二个是参数列表methodInfo.Invoke(o, new object[] { "1" });

调用泛型类中的方法

public void Show(T t, R r, E e){ Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name}");}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息 注意:`3是占位符,该泛型对象中有几个泛型参数Type type = assembly4.GetType("Common.GenericClass`3");//指定泛型对象的参数类型Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) });object oGeneric = Activator.CreateInstance(type1);MethodInfo methodInfo = type1.GetMethod("Show");methodInfo.Invoke(oGeneric, new object[] { 1.02, Guid.NewGuid(), DateTime.Now });

调用泛型类中的泛型方法

public class GenericClass<T, R, E>{ public void Show<X>(T t, R r, E e, X x) { Console.WriteLine($"{t.GetType().Name},{r.GetType().Name},{e.GetType().Name},{x.GetType().Name}"); }}
//加载程序集Assembly assembly4 = Assembly.Load("Common");//获取指定类型对象的(类型)信息 注意:`3是占位符,该泛型对象中有几个泛型参数Type type = assembly4.GetType("Common.GenericClass`3");//指定泛型对象的参数类型Type type1 = type.MakeGenericType(new Type[] { typeof(double), typeof(Guid), typeof(DateTime) });object oGeneric = Activator.CreateInstance(type1);MethodInfo methodInfo = type1.GetMethod("Show");//指定泛型方法的参数类型MethodInfo methodInfo1 = methodInfo.MakeGenericMethod(new Type[] { typeof(string) });methodInfo1.Invoke(oGeneric, new object[] { 1.2, Guid.NewGuid(), DateTime.Now, "" });

反射操作实体字段、属性

public class People{ public People() { Console.WriteLine($"{this.GetType().FullName}构造了一次"); }
public int id { get; set; } public string name { get; set; } public string description { get; set; }}
Type type = typeof(People);object o = Activator.CreateInstance(type);//type.GetFields()也可以,操作也是一样的foreach (var item in type.GetProperties()){ //Console.WriteLine(item.Name);//打印字段属性名 //Console.WriteLine(item.GetValue(o));//打印字段值 if (item.Name.Equals("id")) item.SetValue(o, 1);//设置字段值 if (item.Name.Equals("name")) item.SetValue(o, "111"); Console.WriteLine($"{type.Name}.{item.Name}={item.GetValue(o)}");}

反射映射字段,对象中字段数据转换赋值

public class People{ public People() { Console.WriteLine($"{this.GetType().FullName}构造了一次"); }
public int id { get; set; } public string name { get; set; } public string description { get; set; }}
public class DtoPeople{ public DtoPeople() { Console.WriteLine($"{this.GetType().FullName}构造了一次"); }
public int id { get; set; } public string name { get; set; } public string description { get; set; }}
People people = new People();people.id = 1;people.name = "1";people.description = "";
Type typePeople = typeof(People);Type typeDtoPeople = typeof(DtoPeople);//需要赋值的对象object o = Activator.CreateInstance(typeDtoPeople);foreach (var item in typeDtoPeople.GetFields()){ //找出源类型字段的值 item.Name为字段属性名 object value = typePeople.GetProperty(item.Name).GetValue(people); //赋值 item.SetValue(o, value);}

反射的基础就写到这儿,接下来几篇会利用反射写自定义ORM与IOC组件.

反射的优点:动态获取对象.可扩展

反射的缺点:1.写起来复杂.2.避开编译器的检查,错误风险很大.3.性能不好




以上是关于C# 反射详解一的主要内容,如果未能解决你的问题,请参考以下文章

《C#零基础入门之百识百例》(一百)反射详解 -- 检索特性

《C#零基础入门之百识百例》(一百)反射详解 -- 检索特性

详解C#特性和反射

详解C#中的反射

C#反射机制详解

C# 反射详解一