软件
attributeusage(C#语法——反射,架构师的入门基础)

前言

所以,我认为不要把写代码上升到科学的高度。上升到艺术就可以了,因为艺术本身也没有高度。。。。

有兴趣是好事,但就算知道了反射的本质,了解了反射是如何设计的,你技术也没什么质的改变。因为,技术水平最终还是要落实到应用上。

所以,不论什么事,过度了,总不是好事。

本篇文章主要介绍C#反射【用法】。

反射也是最隐蔽的语法,因为反射写出来后,通常它会被直接封装,然后调用者就只负责使用,不再关注他的具体实现。

反射的定义

看不懂?没关系,我们把它翻译成人类可理解的语言。

而反射,就是相对于这种正向调用的存在。即,它是反向调用。

有同学会问了, 既然正向可以调用,那么反向调用干什么呢?

反射的基础应用

先看下面代码;代码为通过类名称的字符,反射出类的对象。

public class ReflectionSyntax{  public static void Excute() { Type type = GetType("Syntax.Kiba"); Kiba kiba = (Kiba)Activator.CreateInstance(type); Type type2 = GetType2("Syntax.Kiba"); Kiba kiba2 = (Kiba)Activator.CreateInstance(type2); } public static Type GetType(string fullName) { Assembly assembly = Assembly.Load("Syntax"); Type type = assembly.GetType(fullName, true, false); return type; }  public static Type GetType2(string fullName) { Type t = Type.GetType(fullName); return t; } } public class Kiba{  public void PrintName() { Console.WriteLine("Kiba518"); } } 

其中字符串"Syntax.Kiba"是一个完全限定名。什么是完全限定名?完全限定名就是命名空间+类名。在反射的时候,需要我们传递完全限定名来确定到底要去哪个命名空间,找哪个类。

GetType2方法是简单的获取类别,通过Type直接就解析了字符串。而GetType则先进行了加载Assembly(组件),然后再由组件获取类型。

区别是,用Type直接解析,只能解析当前命名空间下的类。如果该类存在于引用的DLL中,就解析不了。

[Assembly.Load指定了程序集名]这句话不好理解?

Assembly

也可以导入我们未引入程序集的dll。调用模式如下:

System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");

有的同学可能会担心性能,会觉得这样反射,会使程序变慢。

那么,到底会不会变慢呢?

即,只要反射时把类的命名空间写全,那么速度就不会慢。

函数的反射应用主要是使用类MethodInfo类反射,下面先看下基础应用。

public static void ExcuteMethod(){  Assembly assembly = Assembly.Load("Syntax");  Type type = assembly.GetType("Syntax.Kiba", true, false); MethodInfo method = type.GetMethod("PrintName");  object kiba = assembly.CreateInstance("Syntax.Kiba"); object[] pmts = new object[] { "Kiba518" }; method.Invoke(kiba, pmts);//执行方法 }public class Kiba{ public string Name { get; set; } public void PrintName(string name) { Console.WriteLine(name); }}

下面讲解一些这些代码。

然后我们通过Assembly创建了一个Kiba的实例,接着定义了一个参数的Object数组,因为Kiba类下的函数PrintName只有一个参数,所以,我们只为这个Object数组添加一个对象[Kiba518]。

这样,函数的反射就实现了。

属性反射是用PropertyInfo类来实现,下面看基础的属性反射。

public static void ExcuteProperty(){ Kiba kiba = new Kiba(); kiba.Name = "Kiba518"; object name = ReflectionSyntax.GetPropertyValue(kiba, "Name"); Console.WriteLine(name); } public static object GetPropertyValue(object obj, string name){ PropertyInfo property = obj.GetType().GetProperty(name); if (property != null) { object drv1 = property.GetValue(obj, null); return drv1; } else { return null; } }

GetPropertyValue函数里通过使用PropertyInfo完成了反射。

别着急,我们接下来一起看反射的架构应用。

框架编写的核心目的之一,是统一系统秩序。那么什么是系统秩序呢?

既然系统由子系统,程序集,类,函数这四个基础元素构成,那么系统秩序,自然指的就是这四个元素的秩序。而这四个元素最难形成秩序的就是函数了。

反射的架构应用

所以,这里只介绍一种实战的架构应用,一种使用反射的框架基础结构。下面请框架基础代码。

public class Client{ public void ExcuteGetNameCommand() { Proxy proxy = new Proxy(); GetNameCommand cmd = new GetNameCommand(); Resultbase rb = proxy.ExcuteCommand(cmd); } } public class Proxy{ public Resultbase ExcuteCommand(Commandbase command) { var result = HandlerSwitcher.Excute(command); return result as Resultbase; }}public class HandlerSwitcher{ private const string methodName = "Excute";//约定的方法名 private const string classNamePostfix = "Handler";//约定的处理Command的类的名称的后缀  //获取命名空间的名称 public static string GetNameSpace(Commandbase command) { Type commandType = command.GetType();//获取完全限定名 string[] CommandTypeNames = commandType.ToString().Split('.'); string nameSpace = ""; for (int i = 0; i < CommandTypeNames.Length - 1; i++) { nameSpace += CommandTypeNames[i]; if (i < CommandTypeNames.Length - 2) { nameSpace += "."; } }  return nameSpace; }  public static object Excute(Commandbase command) { string fullName = command.GetType().FullName;//完全限定名 string nameSpace = GetNameSpace(command);//命名空间  Assembly assembly = Assembly.Load(nameSpace); Type handlerType = assembly.GetType(fullName + classNamePostfix, true, false); object obj = assembly.CreateInstance(fullName + classNamePostfix); MethodInfo handleMethod = handlerType.GetMethod(methodName);//获取函数基本信息 object[] pmts = new object[] { command }; //传递一个参数command try { return handleMethod.Invoke(obj, pmts); } catch (TargetInvocationException tie) { throw tie.InnerException; } }}public class GetNameCommandHandler{ public Resultbase Excute(Commandbase cmd) { GetNameCommand command = (GetNameCommand)cmd; Resultbase result = new Resultbase(); result.Message = "I'm Kiba518"; return result; }}public class GetNameCommand: Commandbase{ } public class Commandbase{  public int UserId { get; set; }   public string UserName { get; set; }   public string ArgIP { get; set; } }public class Resultbase{  public string Message { get; set; } }

即,客户端,不论传来什么样的Command,只要它是继承自Commandbase的,这个代理都会找到对应的处理类,并执行处理,且返回结果。

这个简单的框架中,使用了一个概念,叫做约定优先原则,也叫做约定优于配置;喜欢概念的小伙伴可以自行百度。

第一个是,处理Command的类必须后缀名是Command的类名+Handler结尾。

其实概念就是供大家使用的,会用即可;学习的过程中,概念之类的术语,有个印象即可。

PS:为了阅读方便,这里面的类都集中写在了一个命名空间之下了,如果有想使用这种设计模式的同学,请按照自己项目所需进行扩展。

这样,我们就通过反射实现了一个非常简约的框架,通过使用这个框架,会让代码变的更加简洁。

而为了实现每个模块的简洁,反射也将会被封装在各个模块的底层,所以,反射毫无疑问,就是框架设计的基础。

反射在系统中另一个重要应用就是与特性的结合使用。

而利用反射并结合特性,完全可以简化这种复杂操作的代码量。

public partial class ReflectionSyntax{ public void ExcuteKibaAttribute() { Kiba kiba = new Kiba(); kiba.ClearName = "Kiba518"; kiba.NoClearName = "Kiba518"; kiba.NormalName = "Kiba518"; ClearKibaAttribute(kiba); Console.WriteLine(kiba.ClearName); Console.WriteLine(kiba.NoClearName); Console.WriteLine(kiba.NormalName); } public void ClearKibaAttribute(Kiba kiba) { List<PropertyInfo> plist = typeof(Kiba).GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).ToList();//只获取Public的属性 foreach (PropertyInfo pinfo in plist) { var attrs = pinfo.GetCustomAttributes(typeof(KibaAttribute), false); if (null != attrs && attrs.Length > 0) {  var des = ((KibaAttribute)attrs[0]).Description;  if (des == "Clear") { pinfo.SetValue(kiba, null);  } } } } } public class Kiba{ [KibaAttribute("Clear")] public string ClearName { get; set; } [KibaAttribute("NoClear")] public string NoClearName { get; set; } public string NormalName { get; set; } }[System.AttributeUsage(System.AttributeTargets.All)]public class KibaAttribute : System.Attribute{ public string Description { get; set; } public KibaAttribute(string description) { this.Description = description; }}

当然为了一个属性这么做不值得,但如果一个对象有70个属性的时候,这么做就值得了。

反射+特性最常见的场景

我们在开发中,为了让实体更加充血,往往会对数据实体增加一些属性和方法。(什么是充血?充血就是充血模型,有兴趣的同学可以自行百度了解下,简单说就是为实体加属性和方法。)

如果只是一个实体,那么,多遍历几次也没影响。但,如果是数十万的数据,那这多几次的遍历影响就大了。

讲了这么多为什么不给代码呢?

注意,我这里说的是框架,而不是架构。

结语

答案是,当然有人可以熟练应用。反射是架构师的入门基础,任何一个[可以实战]的架构师,都需要随时随地的可以手写出反射,因为优化框架是他们的责任。

所以,对此有所怀疑的小伙伴,可以努力练习了,将委托融入血液,是高级软件工程师的基础,而将反射融入血液,就是架构师的基础了。


顶一下()     踩一下()

热门推荐

发表评论
0评