C# 知识体系构建| 反射 上

Posted 小小Unity

tags:

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

在前面的小节中,我们快速浏览了一遍C# 1.0版本支持的特性,在浏览的过程中,我补充了一些个人的理解,强调了比较常用且重要的知识和经验,大家已经熟知的内容,就快速略过了。但其实我们漏掉了一些内容。比如在C# 1.0的语言特性中,并没 有说有反射这样的一项特性。这是因为,反射这样的特性,并不是在语言层面上提供的,而是在 .Net中进行的。

C# 1.0明显与反射有关的特性是Attribute,Attribute一般与反射一起使用才会发挥最大的作用。在介绍Attribute之前,作为准备,我们应该先介绍反射这个概念。

第三节,我们的核心内容只有两个字:反射

3.1 反射简介

反射,英文为:Reflection。反射用我自己的理解来介绍其实很简单,核心就是使用各种类型(Type)相关的API。

对于反射来说,其Hello World如下:

 using UnityEngine; namespace QFramework.Example.CSharp {  public class ReflectionExample : MonoBehaviour {  public class SomeClass { }  void Start()  {  var type = typeof(SomeClass);  Debug.Log(type);  }  }} // 输出 // QFramework.Example.CSharp.ReflectionExample+SomeClass

代码很简单,typeof可以获取到一个类的Type。

这是我们接触的第一个Type相关的API。而获取一个类或对象的类型信息,就算是一种反射操作了。

3.1.1 反射的官方定义

前面我给出了自己的理解:反射的核心就是使用各种Type相关的API。

这里我介绍了一个概念How To,即怎么使用,但是并没有介绍这个概念的What,即是什么。如果让我给出自己的理解,目前还给不出,因为我对于反射的知识掌握得并没有那么深入,只能给出How To。

关于反射的What,我们就交给官方来定义吧。下面给出反射的官方定义,如下:提供封装程序集、模块和类型的对象

这句定义是直接翻译过来的,太直白了。我整理了顺序,如下:

  • 反射提供以下对象

    • 封装了程序集的对象

    • 封装了模块的对象

    • 封装了类型的对象

我们简单分析下官方关于反射的定义:在C#中,我们可以通过Type获取到该类所在的程序集、模块、以及本身的类型,代码如下:

using UnityEngine; namespace QFramework.Example.CSharp {  public class ReflectionExample : MonoBehaviour  {  public class SomeClass { }  void Start()  {  var type = typeof(SomeClass); // 程序集  Debug.Log(type.Assembly); // 模块  Debug.Log(type.Module);  // 类型名字  Debug.Log(type.Name);  }  } } // 输出结果: // Assembly-CSharp, Version=0.0.0.0, Culture=neutral, // PublicKeyToken=null // Assembly-CSharp.dll // SomeClass

官方的定义与我的定义重合。

我的定义很简单,就是之前说过的,反射的核心就是使用各种Type相关的API。现在我们简单介绍了官方的定义和我的定义,到这里大家应该还是对反射这个概念还是非常的模糊。

我们会在本节中,慢慢深入去探索反射这个概念,以及它的应用场景和常见使用方式。

3.1.2 知识地图

3.2 Type对象

上面给出了反射的定义:使用各种Type相关的API。

所以如果想更加深入地理解反射这个概念,或者想在反射这个方向上提高自己的能力,我们要做的当然是先学习一些常用的Type相关的API。后续会对Type相关的API进行一些实践。

首先,就是Type对象。在上文的示例中,我们可以通过typeof这个API获取一个Type对象。通过Type对象,我们可以访问Type的程序集、模块、类名等信息。示例代码如下:

using UnityEngine; namespace QFramework.Example.CSharp { public class ReflectionExample : MonoBehaviour {  public class SomeClass { }  void Start()  {  var type = typeof(SomeClass);  // 程序集  Debug.Log(type.Assembly);  // 模块  Debug.Log(type.Module);  // 类型名字  Debug.Log(type.Name);  } } } // 输出结果: // Assembly-CSharp, Version=0.0.0.0, // Culture=neutral, PublicKeyToken=null // Assembly-CSharp.dll // SomeClass

接下来,我们再写一个示例,看看Type还可以拿到哪些信息。

public class ReflectionExample : MonoBehaviour {  public class SomeClass { }  void Start() {  // 通过对象的 GetType 获取对象  var type = new SomeClass().GetType();  Debug.LogFormat("FullName:{0}", type.FullName);  Debug.LogFormat("IsClass:{0}", type.IsClass);  Debug.LogFormat("Namespace:{0}", type.Namespace);  // 是否是抽象的(抽象类、接口)  Debug.LogFormat("IsAbstract:{0}", type.IsAbstract); // 是否是值类型  Debug.LogFormat("IsValueType:{0}", type.IsValueType);  }  } // 输出结果: //FullName:QFramework.Example.CSharp.ReflectionExample+SomeC//lass // IsClass:True // Namespace:QFramework.Example.CSharp // IsAbstract:False // IsValue
Type:False

这次的代码中我们发现,可以通过一个对象的GetType()获取到Type对象。除了这个关键点,Type还可以访问大量的类相关信息,比如是否是Class?是否是抽象的?是否是值类型等等。

在这里,我们把这种判断是否是某一个事物的API,归类为检测API(我定义的名字)。而获取一些类名、命名空间名等,这样的API,归类为类信息查询API(我定义的名字)。除了这两个,还有一种API,就是获取父类类型、获取方法、获取成员变量 的API,这些API归类为类结构查询API(我定义的名字)。

总结如下:

  • 类信息查询API:获取各种名字。

  • 检测API:判断Type是否是某一个事物(比如是否是抽象的,等等)。

  • 类结构查询API:获取父类类型、获取方法、获取构造、获取成员变量等等。

关于Type的API,我们有了一个基本的结构体系。

3.2.1 知识地图

C# 知识体系构建(三)| 反射 上

3.3 类结构查询API

示例代码如下:

using UnityEngine; namespace QFramework.Example.CSharp {  public class ReflectionExample : MonoBehaviour  {  public class SomeClass  {  public void MethodA() { }  private void MethodB() { }  public string FieldA;  public string PropertyA { get; set; }  }  void Start()  {  // 通过对象的 GetType 获取对象 
var type = new SomeClass().GetType(); Debug.LogFormat("BaseType:{0}", type.BaseType); Debug.LogFormat("Methods Length:{0}", type.GetMethods().Length); Debug.LogFormat("Fields Length:{0}", type.GetFields().Length); Debug.LogFormat("Properties Length:{0}", type.GetProperties().Length); Debug.LogFormat("Members Length:{0}", type.GetMembers().Length); } }} // 输出结果: // BaseType:System.Object // Methods Length:7 // Fields Length:1 // Properties Length:1// Members Length:10

代码其实很简单,这里只展示了一部分的类结构查询API。

首先通过type.BaseType可以访问到Type的父类类型,是System.Object。接着Methods的个数是7,这个数字比较奇怪,我们在SomeClass中只定义了2个Method,有可能父类的方法,或者是构造方法,我们会在接下来进行一个测试。

然后是Fields,Fields就是所谓的字段,指的是指成员变量,不过这里要注意一点,GetFields 默认返回的是访问权限为Public的成员变量,如果想算上非Public就需要在获取的时候输入一些Flag,这个我们在之后介绍,这里我们只要知道字段就是指成 员变量,而GetFIelds默认只返回Public的成员变量。

再然后是Properties,也就是属性器,返回了1个没有问题。最后是Members,也就是成员,成员包括成员变量和成员方法,Members的个数为10,这一点也需要我们在接下来做一些测试。

到此,有两个疑问,一个是Methods的数量和Members的数量,还有一个需要了解的就是获取信息的时候是可以传入一些Flag的。

总结如下:

  • Methods数量验证

  • Members数量验证

  • 获取信息时传入Flag

接下来,我们优先把上边的三个问题了解清楚。

3.3.1 知识地图

3.4GetMethods()验证与BingingFlags初识

3.4.1 Methods验证

代码如下:

using UnityEngine; namespace QFramework.Example.CSharp {  public class ReflectionExample : MonoBehaviour  {  public class SomeClass  {  public void MethodA() { }  private void MethodB() { }  protected void MethodC() { }  public string PropertyA { get; set; }  private string PropertyB { get; set; } }  void Start()  {  // 通过对象的 GetType 获取对象  var type = typeof(SomeClass);  Debug.LogFormat("Methods Count:{0}", type.GetMethods().Length);  foreach (var methodInfo in type.GetMethods())  {  Debug.LogFormat("Method Name:{0}", methodInfo.Name); }  }  } } // 输出结果 // Methods Count:7 // Method Name:MethodA // Method Name:get_PropertyA // Method Name:set_PropertyA // Method Name:Equals // Method Name:GetHashCode // Method Name:GetType // Method Name:ToString

我们发现,Methods中只包含Public权限的成员方法,而Public权限的属性器也被当做两个Method了。

GetMethods除了获取了类中的方法,也获取了父类中的方法,比如Equals、GetHashCode、GetType、ToString。到这里我们可能会产生一些疑问,

比如:

  • GetMethods可不可以获取Private权限的成员方法? 

  • GetMethods可不可以不获取父类的方法?

答案都是可以的。我们来写一些测试。

代码如下:

using System.Reflection; using UnityEngine; namespace QFramework.Example.CSharp {  public class ReflectionExample : MonoBehaviour  {  public class SomeClass  {  public void MethodA() { }  private void MethodB() { }  protected void MethodC() { }  public string PropertyA { get; set; }  private string PropertyB { get; set; }  }  void Start()  {  // 通过对象的 GetType 获取对象  var type = typeof(SomeClass);  // 获取私有对象  // NonPublic 包含非 public 权限的方法  // Public 包含 public 权限的方法  // DeclaredOnly 不包含父类的方法  // Instance 指定实例成员将包括在搜索中。
var resultMethods = type.GetMethods( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); foreach (var methodInfo in resultMethods) { Debug.LogFormat("Method Name:{0}", methodInfo.Name); } } } } // 输出结果: // Method Name:MethodA // Method Name:MethodB // Method Name:MethodC // Method Name:get_PropertyA // Method Name:set_PropertyA // Method Name:get_PropertyB // Method Name:set_PropertyB

代码其实比较简单,核心的部分就是BindingFlags的使用。在通过Type获取一些类结构信息的时候,BindingFlags可以优化我们的查询结果。

到此,我们应该解决了两个问题:

  • Methods数量验证

  • 获取信息时传入Flag

那么还剩最后一个问题:

  • Members数量验证

3.4.2 知识地图




以上是关于C# 知识体系构建| 反射 上的主要内容,如果未能解决你的问题,请参考以下文章

关于如何构建自己的只是体系

C# 获取DLL中需要的接口类

农码一生博文索引

c# WinForm 全局键盘事件怎么监听。。。。会的童鞋们贴上代码和用例吧,感激

反射基本知识

Android知识体系梳理笔记三:动态代理模式---插件加载机制学习笔记