• 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层信息

     根据反射创建对象

    //加载程序集
    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.性能不好

    bye-bye.

     Grinning Cat with Smiling Eyes

  • 相关阅读:
    设计模式学习笔记--原型模式
    设计模式学习笔记--工厂方法模式
    复制、粘贴一个物体的所有组件
    设计模式学习笔记--装饰模式
    模板方法模式(TemplateMethod)
    FreeSql 与 SqlSugar 性能测试(增EFCore测试结果)
    FreeSql 新查询功能介绍
    FreeSql 过滤器使用介绍
    非常贴心的轮子 FreeSql
    .NETCore 下支持分表分库、读写分离的通用 Repository
  • 原文地址:https://www.cnblogs.com/zhangnever/p/13056181.html
Copyright © 2020-2023  润新知