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

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

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

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

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

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

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

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

    反射的具体用法

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

    1 using System;
    2 
    3 namespace IRefTest
    4 {
    5     public interface IAnimal
    6     {
    7         string CallName();
    8     }
    9 }

    新建项目:DogRefTest 新建类 Dog

     1 using IRefTest;
     2 using System;
     3 
     4 namespace DogRefTest
     5 {
     6     public class Dog: IAnimal
     7     {
     8         public Dog()
     9         {
    10             this.name = "无名小狗";
    11         }
    12 
    13         public string name { set; get; }
    14         public int Age { set; get; }
    15 
    16         public string food;
    17 
    18         private int foot;
    19 
    20         public string CallName()
    21         {
    22             Console.WriteLine($"狗叫:{this.name}");
    23             return this.name;
    24         }
    25     }
    26 }

    新建项目:CatRefTest 新建类 Cat

     1 using IRefTest;
     2 using System;
     3 
     4 namespace CatRefTest
     5 {
     6     public sealed class Cat : IAnimal
     7     {
     8         public Cat()
     9         {
    10             this.name = "无名小猫";
    11         }
    12         public Cat(string name)
    13         {
    14             this.name = name ?? throw new ArgumentNullException(nameof(name));
    15         }
    16 
    17         public string name { set; get; }
    18         /// <summary>
    19         /// 公开无参方法
    20         /// </summary>
    21         /// <returns></returns>
    22         public string CallName()
    23         {
    24             Console.WriteLine($"猫叫:{this.name}");
    25             return this.name;
    26         }
    27         /// <summary>
    28         /// 公开单参数方法
    29         /// </summary>
    30         /// <param name="what"></param>
    31         public void CallWhatPublic(string what)
    32         {
    33             Console.WriteLine($"公开单参数方法:{what}");
    34         }
    35         /// <summary>
    36         /// 私有单参数方法
    37         /// </summary>
    38         /// <param name="what"></param>
    39         private void CallWhatPrivate(string what)
    40         {
    41             Console.WriteLine($"私有单参数方法:{what}");
    42         }
    43 
    44     }
    45 }

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

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

    新建类AnimalFactory

     1 using IRefTest;
     2 using System;
     3 using System.Configuration;
     4 using System.Reflection;
     5 
     6 namespace Util_YCH.Build.Reflection
     7 {
     8     public class AnimalFactory
     9     {
    10         private static string IAniamlConfig = ConfigurationManager.AppSettings["IAnimalConfig"];
    11         private static string DLLName = IAniamlConfig.Split(',')[0];
    12         private static string TypeName = IAniamlConfig.Split(',')[1];
    13 
    14         public static IAnimal GetAnimal() {
    15             Assembly assembly = Assembly.LoadFrom(DLLName);
    16             Type type = assembly.GetType(TypeName);//完全限定名
    17             var obj = Activator.CreateInstance(type);
    18             IAnimal animal = (IAnimal)obj;
    19             return animal;
    20         }
    21     }
    22 }

    main方法中输入代码并运行

     1 using Util_YCH.Build.Reflection;
     2 
     3 namespace Util_YCH.Build
     4 {
     5     class Program
     6     {
     7         static void Main(string[] args)
     8         {
     9             var animal = AnimalFactory.GetAnimal();
    10             animal.CallName();//输出:
    11         }
    12     }
    13 }

    输出

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

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

    运行,输出

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

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

    如何通过反射调用方法?

    添加一个 泛型类 Generic_Ref

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Text;
     4 
     5 namespace CatRefTest
     6 {
     7     public class Generic_Ref<T>
     8     {
     9         /// <summary>
    10         /// 泛型方法
    11         /// </summary>
    12         /// <typeparam name="T"></typeparam>
    13         /// <param name="t"></param>
    14         public void CallOne(T t)
    15         {
    16             Console.WriteLine($"泛型方法反射了:{t.GetType().FullName}");
    17         }
    18         /// <summary>
    19         /// 泛型方法
    20         /// </summary>
    21         /// <typeparam name="T"></typeparam>
    22         /// <param name="t"></param>
    23         public void Call<K,V>(K k,V v)
    24         {
    25             Console.WriteLine($"泛型方法反射了,K:{k.GetType().FullName},V:{v.GetType().FullName}");
    26         }
    27     }
    28 }

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

     1 #region 反射方法
     2             #region 无参函数调用
     3             {
     4                 MethodInfo method = type.GetMethod("CallName");
     5                 method.Invoke(obj, null);
     6             }
     7             #endregion
     8 
     9             #region 有参函数反射调用
    10             {
    11                 MethodInfo method = type.GetMethod("CallWhatPublic");
    12                 method.Invoke(obj, new object[] { "反射运行了?" });
    13             }
    14             #endregion
    15 
    16             #region 私有参函数反射调用
    17             {
    18                 MethodInfo method = type.GetMethod("CallWhatPrivate", BindingFlags.Instance | BindingFlags.NonPublic);
    19                 method.Invoke(obj, new object[] { "反射运行了?" });
    20             }
    21             #endregion
    22 
    23             #region 泛型方法反射
    24             {
    25                 Type typeT = assembly.GetType("CatRefTest.Generic_Ref`1");//完全限定名
    26                 Type typeG = typeT.MakeGenericType(new Type[] { typeof(int) });
    27                 var objG = Activator.CreateInstance(typeG);
    28                 MethodInfo method = typeG.GetMethod("CallOne");
    29                 method.Invoke(objG, new object[] { 100 });
    30             }
    31             #endregion
    32 
    33 
    34             #region 泛型方法反射
    35             {
    36                 Type typeT = assembly.GetType("CatRefTest.Generic_Ref`1");//完全限定名
    37                 Type typeG = typeT.MakeGenericType(new Type[] { typeof(int) });
    38                 var objG = Activator.CreateInstance(typeG);
    39                 MethodInfo method = typeG.GetMethod("Call");
    40                 MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(string), typeof(bool) });
    41                 methodNew.Invoke(objG, new object[] { "hah0", false });
    42             }
    43             #endregion
    44             #endregion
    45 
    46             #region 反射属性
    47             {
    48                 Type typeDog = typeof(DogRefTest.Dog);
    49                 var theDog = Activator.CreateInstance(typeDog);
    50                 Console.WriteLine("属性");
    51                 foreach (var pop in typeDog.GetProperties())
    52                 {
    53                     Console.WriteLine(pop.Name);
    54                     if (pop.Name.Equals("name"))
    55                     {
    56                         pop.GetValue(theDog);
    57                         pop.SetValue(theDog,"反射的狗");
    58                     }
    59                     else if (pop.Name.Equals("Age"))
    60                     {
    61                         pop.GetValue(theDog);
    62                         pop.SetValue(theDog, 5);
    63                     }
    64                 }
    65                 Console.WriteLine("字段");
    66                 foreach (var fieId in typeDog.GetFields(BindingFlags.Instance|BindingFlags.Public| BindingFlags.NonPublic))
    67                 {
    68                     Console.WriteLine(fieId.Name);
    69                     if (fieId.Name.Equals("food"))
    70                     {
    71                         fieId.GetValue(theDog);
    72                         fieId.SetValue(theDog, "大骨头");
    73                     }
    74                     else if (fieId.Name.Equals("foot"))
    75                     {
    76                         fieId.GetValue(theDog);
    77                         fieId.SetValue(theDog, 4);
    78                     }
    79                 }
    80 
    81                 var theDogDto = new Mapper<DogRefTest.DogDto>().MapTo((DogRefTest.Dog)theDog);
    82             }
    83             #endregion

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

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

     1 public interface IMapper<T> {
     2         T MapTo<V>(V v) where V : class;
     3     }
     4     public class Mapper<T>:IMapper<T> where T : class
     5     {
     6         #region 利用反射进行自动映射
     7         public T MapTo<V>(V v)
     8             where V : class
     9         {
    10             Type typeIn = typeof(V);
    11             Type typeOut = typeof(T);
    12             var typeOutObj = Activator.CreateInstance(typeOut);
    13 
    14             foreach (var pop in typeOut.GetProperties())
    15             {
    16                 Console.WriteLine(pop.Name);
    17                 var popIn = typeIn.GetProperty(pop.Name);
    18                 if (popIn is null)
    19                     throw new Exception($"{pop.Name} 无法进行映射");
    20                 var value = popIn.GetValue(v);
    21                 Console.WriteLine($"对象v中的对应值是{pop}");
    22                 pop.SetValue(typeOutObj, value);
    23             }
    24 
    25             foreach (var field in typeOut.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
    26             {
    27                 Console.WriteLine(field.Name);
    28                 var popIn = typeIn.GetField(field.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    29                 if (popIn is null)
    30                     throw new Exception($"{field.Name} 无法进行映射");
    31                 var value = popIn.GetValue(v);
    32                 Console.WriteLine($"对象v中的对应值是{field}");
    33                 field.SetValue(typeOutObj, value);
    34             }
    35             return (T)typeOutObj;
    36         }
    37         #endregion
    38     }

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

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

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

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

  • 相关阅读:
    闭包的一个经典例子
    手机归属地查询API
    安卓模拟器导入通讯录
    使用RazorGenerator实现项目模块分离
    .met mvc 一种判断是否已登录的方式
    stl 空间配置器理解
    STL 生成全排列
    KMP算法理解
    解决八皇后问题,递归与非递归方式两种
    获取第K大的数
  • 原文地址:https://www.cnblogs.com/lgx5/p/12321073.html
Copyright © 2011-2022 走看看