zoukankan      html  css  js  c++  java
  • C#进阶之反射(Reflection)

    首先说一下反射的优点:动态!!!

    首先了解一下C#编译运行过程,大致如下所示:

     首先被编译器编译成dll/exe,一般我们发布的都是这个东西,然后在运行的时候会被CLR/JIT编译成机器码。

    为什么不直接通过编译器编译成机器码呢?答案就是:通过CLR/JIT可以根据不同的平台编译成不同的机器码,用以一次编译多平台运行。

    微软提供的反射工具主要是 System.Reflection

    加载dll的具体用法大致如下

                Assembly assembly1 = Assembly.LoadFile(@"D:戎光科技Util_YCH.ConsoleRefTestinDebug
    etstandard2.0RefTest.dll");//完整路径
                Assembly assembly2 = Assembly.Load(@"RefTest");//程序集名称,不带后缀
                //既可以是完整路径也可以是程序集完整名称
                Assembly assembly3 = Assembly.LoadFrom(@"D:戎光科技Util_YCH.ConsoleRefTestinDebug
    etstandard2.0RefTest.dll");
                Assembly assembly = Assembly.LoadFrom(@"RefTest.dll");

    反射的具体用法

    新建一个项目:AnimalRefTest 新建接口IAnimal

    using System;
    
    namespace IRefTest
    {
        public interface IAnimal
        {
            string CallName();
        }
    }

    新建项目:DogRefTest 新建类 Dog

    using IRefTest;
    using System;
    
    namespace DogRefTest
    {
        public class Dog: IAnimal
        {
            public Dog()
            {
                this.name = "无名小狗";
            }
    
            public string name { set; get; }
            public int Age { set; get; }
    
            public string food;
    
            private int foot;
    
            public string CallName()
            {
                Console.WriteLine($"狗叫:{this.name}");
                return this.name;
            }
        }
    
    
    
    }

    新建项目:CatRefTest 新建类 Cat

    using IRefTest;
    using System;
    
    namespace CatRefTest
    {
        public sealed class Cat : IAnimal
        {
            public Cat()
            {
                this.name = "无名小猫";
            }
            public Cat(string name)
            {
                this.name = name ?? throw new ArgumentNullException(nameof(name));
            }
    
            public string name { set; get; }
            /// <summary>
            /// 公开无参方法
            /// </summary>
            /// <returns></returns>
            public string CallName()
            {
                Console.WriteLine($"猫叫:{this.name}");
                return this.name;
            }
            /// <summary>
            /// 公开单参数方法
            /// </summary>
            /// <param name="what"></param>
            public void CallWhatPublic(string what)
            {
                Console.WriteLine($"公开单参数方法:{what}");
            }
            /// <summary>
            /// 私有单参数方法
            /// </summary>
            /// <param name="what"></param>
            private void CallWhatPrivate(string what)
            {
                Console.WriteLine($"私有单参数方法:{what}");
            }
    
        }
    }

    新建一个项目RefTest,新建配置文件,添加内容

        <add key="IAnimalConfig" value="CatRefTest,CatRefTest.Cat"/>

    新建类AnimalFactory

    using IRefTest;
    using System;
    using System.Configuration;
    using System.Reflection;
    
    namespace Util_YCH.Build.Reflection
    {
        public class AnimalFactory
        {
            private static string IAniamlConfig = ConfigurationManager.AppSettings["IAnimalConfig"];
            private static string DLLName = IAniamlConfig.Split(',')[0];
            private static string TypeName = IAniamlConfig.Split(',')[1];
    
            public static IAnimal GetAnimal() {
                Assembly assembly = Assembly.LoadFrom(DLLName);
                Type type = assembly.GetType(TypeName);//完全限定名
                var obj = Activator.CreateInstance(type);
                IAnimal animal = (IAnimal)obj;
                return animal;
            }
        }
    }

    main方法中输入代码并运行

    using Util_YCH.Build.Reflection;
    
    namespace Util_YCH.Build
    {
        class Program
        {
            static void Main(string[] args)
            {
                var animal = AnimalFactory.GetAnimal();
                animal.CallName();//输出:
            }
        }
    }

    输出

     如果修改 配置文件的内容为

        <!--<add key="IAnimalConfig" value="CatRefTest,CatRefTest.Cat"/>-->
        <add key="IAnimalConfig" value="DogRefTest,DogRefTest.Dog"/>

    运行,输出

    感觉和IOC有点像啊,应该是用了类似的方法实现的。

    这样的话,就意味着,如果我们软件设计之初只支持Cat类,但是后来需求变更,需要支持Dog,那么我们只需要修改配置文件就可以在不修改源代码的情况下,只需要在根目录添加DogRefTest.dll,并更新配置文件即可支持,实现热更新。

    如何通过反射调用方法?

    添加一个 泛型类 Generic_Ref

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace CatRefTest
    {
        public class Generic_Ref<T>
        {
            /// <summary>
            /// 泛型方法
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="t"></param>
            public void CallOne(T t)
            {
                Console.WriteLine($"泛型方法反射了:{t.GetType().FullName}");
            }
            /// <summary>
            /// 泛型方法
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="t"></param>
            public void Call<K,V>(K k,V v)
            {
                Console.WriteLine($"泛型方法反射了,K:{k.GetType().FullName},V:{v.GetType().FullName}");
            }
        }
    }

    在AnimalFactory的GetAnimal()中添加如下代码

                #region 反射方法
                #region 无参函数调用
                {
                    MethodInfo method = type.GetMethod("CallName");
                    method.Invoke(obj, null);
                }
                #endregion
    
                #region 有参函数反射调用
                {
                    MethodInfo method = type.GetMethod("CallWhatPublic");
                    method.Invoke(obj, new object[] { "反射运行了?" });
                }
                #endregion
    
                #region 私有参函数反射调用
                {
                    MethodInfo method = type.GetMethod("CallWhatPrivate", BindingFlags.Instance | BindingFlags.NonPublic);
                    method.Invoke(obj, new object[] { "反射运行了?" });
                }
                #endregion
    
                #region 泛型方法反射
                {
                    Type typeT = assembly.GetType("CatRefTest.Generic_Ref`1");//完全限定名
                    Type typeG = typeT.MakeGenericType(new Type[] { typeof(int) });
                    var objG = Activator.CreateInstance(typeG);
                    MethodInfo method = typeG.GetMethod("CallOne");
                    method.Invoke(objG, new object[] { 100 });
                }
                #endregion
    
    
                #region 泛型方法反射
                {
                    Type typeT = assembly.GetType("CatRefTest.Generic_Ref`1");//完全限定名
                    Type typeG = typeT.MakeGenericType(new Type[] { typeof(int) });
                    var objG = Activator.CreateInstance(typeG);
                    MethodInfo method = typeG.GetMethod("Call");
                    MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(string), typeof(bool) });
                    methodNew.Invoke(objG, new object[] { "hah0", false });
                }
                #endregion
                #endregion
    
                #region 反射属性
                {
                    Type typeDog = typeof(DogRefTest.Dog);
                    var theDog = Activator.CreateInstance(typeDog);
                    Console.WriteLine("属性");
                    foreach (var pop in typeDog.GetProperties())
                    {
                        Console.WriteLine(pop.Name);
                        if (pop.Name.Equals("name"))
                        {
                            pop.GetValue(theDog);
                            pop.SetValue(theDog,"反射的狗");
                        }
                        else if (pop.Name.Equals("Age"))
                        {
                            pop.GetValue(theDog);
                            pop.SetValue(theDog, 5);
                        }
                    }
                    Console.WriteLine("字段");
                    foreach (var fieId in typeDog.GetFields(BindingFlags.Instance|BindingFlags.Public| BindingFlags.NonPublic))
                    {
                        Console.WriteLine(fieId.Name);
                        if (fieId.Name.Equals("food"))
                        {
                            fieId.GetValue(theDog);
                            fieId.SetValue(theDog, "大骨头");
                        }
                        else if (fieId.Name.Equals("foot"))
                        {
                            fieId.GetValue(theDog);
                            fieId.SetValue(theDog, 4);
                        }
                    }
    
                    var theDogDto = new Mapper<DogRefTest.DogDto>().MapTo((DogRefTest.Dog)theDog);
                }
                #endregion

    即可实现反射调用方法以及设置属性字段。

    顺便手写了一个初级的映射方法

    public interface IMapper<T> {
            T MapTo<V>(V v) where V : class;
        }
        public class Mapper<T>:IMapper<T> where T : class
        {
            #region 利用反射进行自动映射
            public T MapTo<V>(V v)
                where V : class
            {
                Type typeIn = typeof(V);
                Type typeOut = typeof(T);
                var typeOutObj = Activator.CreateInstance(typeOut);
    
                foreach (var pop in typeOut.GetProperties())
                {
                    Console.WriteLine(pop.Name);
                    var popIn = typeIn.GetProperty(pop.Name);
                    if (popIn is null)
                        throw new Exception($"{pop.Name} 无法进行映射");
                    var value = popIn.GetValue(v);
                    Console.WriteLine($"对象v中的对应值是{pop}");
                    pop.SetValue(typeOutObj, value);
                }
    
                foreach (var field in typeOut.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
                {
                    Console.WriteLine(field.Name);
                    var popIn = typeIn.GetField(field.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                    if (popIn is null)
                        throw new Exception($"{field.Name} 无法进行映射");
                    var value = popIn.GetValue(v);
                    Console.WriteLine($"对象v中的对应值是{field}");
                    field.SetValue(typeOutObj, value);
                }
                return (T)typeOutObj;
            }
            #endregion
        }

    最后总结一下反射的缺点:

    • 写起来复杂
    • 逃脱了编译器的检查,出错概率高
    • 性能问题,与直接调用之间性能差距可能百倍之多,但是大部分情况下不会影响程序的性能

    反射的实际应用:MVC的路由,EF

    这些应用可以空间换时间,第一次加载完直接存入缓存即可大大提高性能。

  • 相关阅读:
    时间工时累加
    python读取mnist
    开始学习haskell
    pip升级所有packages
    ImportError: numpy.core.multiarray failed to import
    NumPy for MATLAB users
    Spyder
    初学ObjectiveC
    Matlab闭包
    ObjeciveC 内存管理
  • 原文地址:https://www.cnblogs.com/yuchenghao/p/12099891.html
Copyright © 2011-2022 走看看