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 知识地图
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# 知识体系构建| 反射 上的主要内容,如果未能解决你的问题,请参考以下文章