C#反射用法浅述

1. 反射的定义

   反射的官方定义是指程序在运行时可以访问, 检测和修改它本身状态或行为的一种能力. 反射的主要用途就是使给定的程序动态的适应不同的运行情况. 在C#中, 反射最基础的API就是typeof(), 它可以提供封装的程序集, 模块和类型的对象.

using UnityEngine;

namespace Assets
{
    public class Reflect : MonoBehaviour
    {
        public class test { }
        void Start()
        {
            var type = typeof(test);
            var assembly = type.Assembly;
            var module = type.Module;
        }
    }
}

   以上是最基础的反射代码, type即为封装了test类的Type对象, assembly和module分别为test类所在的程序集和模块, 拿到基本的Type对象后, 我们就可以用相关的API实现更多的用途

2. 使用Type对象

   Type类包含在System命名空间内, 反编译后可以看到这个类里包含了一百多个属性, 字段和函数. 熟练地使用这些几乎可以完成运行时所有想要的操作, 这里介绍几个基础, 常用的API.

public class test 
{ 
    public int a;
    public int b { get; }
    public void C() {  }
}
var type = typeof(test);
Debug.LogError("Fields:" + type.GetFields().Length);
Debug.LogError("Properties:"+ type.GetProperties().Length);
Debug.LogError("Methods:"+ type.GetMethods().Length);
Debug.LogError("Field:" + type.GetField("a").Name);
Debug.LogError("Property:" + type.GetProperty("b").Name);
Debug.LogError("Method:" + type.GetMethod("C").Name);

   先在我们的test类里加一些字段属性和函数, 然后使用Type对象的Get___s接口来获取封装类里全部的字段, 属性和函数, 并且分别打印它们的个数, 之后我们通过名字获取到具体的字段属性和函数并且打印它的名字.
在这里插入图片描述
   运行后发现有五个Log和我们想的应该是一样, 但是在test类中我们只定义了一个method, 打印的log却获取到了6个. 我们可以接着打印一下这些methods的名字, 看一看到底是哪些.

var type = typeof(test);
var methods = type.GetMethods();
Debug.LogError("Methods:"+ methods.Length);
foreach (var method in methods) 
{
    Debug.LogError(method.Name);
}

在这里插入图片描述
   我们发现属性的get方法, 以及父类里继承的几个方法里被GetMethods获取到了. 为了不被这些我们不想要的方法影响, 在获取时我们可以加入BindingFlags, 它是存在System.Reflection命名空间里的一个枚举.

public enum BindingFlags
{
    Default = 0,
    IgnoreCase = 1,
    DeclaredOnly = 2,
    Instance = 4,
    Static = 8,
    Public = 16,
    NonPublic = 32,
    FlattenHierarchy = 64,
    InvokeMethod = 256,
    CreateInstance = 512,
    GetField = 1024,
    SetField = 2048,
    GetProperty = 4096,
    SetProperty = 8192,
    PutDispProperty = 16384,
    PutRefDispProperty = 32768,
    ExactBinding = 65536,
    SuppressChangeType = 131072,
    OptionalParamBinding = 262144,
    IgnoreReturn = 16777216
}

枚举值得具体含义就不赘述了, 反编后有注释. 于是加入BindingFlags后, 我们可以得到

var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

在这里插入图片描述
到这里可能会想, 既然获取到了test类里的C方法, 我们能否调用它呢, 答案是肯定的. 我们修改一下代码, 运行!

public class test 
{ 
    public int a;
    public int b { get; }
    public int C(int i) { return i * i; }
}
var t = new test();
var type = t.GetType();
var method = type.GetMethod("C");
object[] pa = new object[] {15 };
Debug.LogError(method.Invoke(t, pa));

在这里插入图片描述

3. Assembly的使用

   之前提到Assembly就是程序集, 它包含了经过C#编译后可以直接在.Net环境中运行的代码, 可以直接加载到项目中, 我们在写代码时候就可以直接获取这个程序集并且使用它提供的API. 如果是要在运行时动态加载, 就需要反射来实现.
几种获取程序集的方法:

var type = typeof(test);
var assembly1 = type.Assembly;						//test类的程序集
var assembly2 = Assembly.GetExecutingAssembly();	//正在运行的程序集
var assembly3 = Assembly.LoadFile("assemblyPath");	//从assemblyPath路径加载程序集

其中动态加载还有多种Load方式, 可以根据具体需求查询. 获取到程序集后, 我们就可以通过API来创建实例或者获取类等等.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值