详解C#反射(Reflection)
一.反射概述
1.1 自己对反射掌握程度的要求
了解反射相关概念,API,阅读代码和文档时看到反射相关内容能反应过来即可
1.2 反射的概念
反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。
这里需要重点理解的一句话,反射可以动态的创建类型的实例。那么什么是静态创建类型的实例呢?通俗的说就是最常见的 类名 对象名 = new 类名() 的形式
要理解为什么要动态创建类型的实例,需要先了解程序集的概念
1.3 程序集概述
- 程序集采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块
- 在 .NET中,可从一个或多个源代码文件生成程序集,程序集可以包含一个或多个模块。
- 程序集以 .exe 或 .dll 文件的形式实现 。
- 程序集使用方式一:静态引用(见下图所示), 引用程序集后,应用程序可以使用其名称空间的所有可访问类型、属性、方法和其他成员,就好像它们的代码是源文件的一部分一样。
生成dll01.dll的代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace dll01
{
public class Player01
{
private int id = 1011112222;
public void Work()
{
Console.WriteLine("id:" + id);
}
}
}
可以看出,VS引用dll后,可以常规使用其Class
参考:.NET 中的程序集
1.4 动态加载程序集
下列示例演示动态加载并使用程序集:用代码在需要的位置加载程序集,在不知道程序集命名空间,类名,方法的情况下,创建对象,调用方法。
通过这个例子,可以知道在有常规 类名.new() 创建对象的情况下,为什么还有反射创建对象的需要。
namespace LearnCS1
{
class MainClass
{
static void Main()
{
//step1.加载程序集
Assembly a1 = Assembly.Load("dll01");
//step2.获取所有类名
Type[] allClass = a1.GetTypes();
foreach (var oneClass in allClass)
{
//step3.根据类名创建对象
object obj = a1.CreateInstance(oneClass.FullName);
//step4.获取所有方法
MethodInfo[] allMethodInfo = oneClass.GetMethods();
foreach (var oneMethod in allMethodInfo)
{
//step5.调用无参方法
if (oneMethod.ReturnParameter.ParameterType.Name == "Void")
oneMethod.Invoke(obj, null);
}
}
Console.ReadKey();
}
}
}
1.5反射的用途
-
需要访问程序元数据的属性。
-
检查和实例化程序集中的类型。
-
在运行时构建新类型。使用System.Reflection.Emit中的类。
-
执行后期绑定,访问在运行时创建的类型的方法。
参考C#官方手册:
反射(C# 编程指南)
.NET 中的反射
二.反射核心类System.Type用法
2.1 获取Class的基础信息
namespace LearnCS1
{
class CTest { }
class MainClass
{
static void Main()
{
//获取Class的基础信息
Type t1 = typeof(CTest); //typeof的参数是类名
Console.WriteLine("type.Name:" + t1.Name);
Console.WriteLine("type.FullName:" + t1.FullName);
Console.WriteLine("type.Namespace:" + t1.Namespace);
Console.WriteLine("type.IsAbstract:" + t1.IsAbstract);
Console.WriteLine("type.IsClass:" + t1.IsClass);
Console.WriteLine("type.IsEnum:" + t1.IsEnum);
Console.WriteLine("type.IsInterface:" + t1.IsInterface);
Console.WriteLine("type.IsSealed:" + t1.IsSealed);
Console.WriteLine("type.IsValueType:" + t1.IsValueType);
Console.WriteLine("type.IsGenericType:" + t1.IsGenericType);
Console.WriteLine("type.AssemblyQualifiedName:" + t1.AssemblyQualifiedName);
}
}
}
2.2 获取public字段
测试类如下
namespace ns_UserInfo
{
public class UserInfo
{
private int _num = 0;
public string Phone = "15911112222";
public string Name { get; set; }
public string Address { get; set; }
public UserInfo()
{
}
public UserInfo(string name)
{
}
public int PublicMethod()
{
return int.MinValue;
}
internal void InternalMethod()
{
}
private void PrivateMethod()
{
}
}
}
using System;
using System.Reflection; //引入反射命名空间
using ns_UserInfo;
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
//获取Public字段
FieldInfo[] fieldInfos = type.GetFields();
foreach (var item in fieldInfos)
{
Console.WriteLine("字段:" + item.Name + " 类型:"+item.FieldType);
}
}
}
}
2.3 获取public属性
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
//获取Public属性
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (var item in propertyInfos)
{
Console.WriteLine("属性:" + item.Name + " 类型:"+item.PropertyType);
}
}
}
}
2.4 获取public方法
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
//获取Public方法
MethodInfo[] methodInfos = type.GetMethods();
foreach (var item in methodInfos)
{
Console.WriteLine("方法:" + item.Name + " 类型:"+item.ReturnType);
}
}
}
}
2.5 获取public构造函数
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
ConstructorInfo[] constructorInfos = type.GetConstructors();//GetConstructors()获取所有的公共的构造函数
for (int i = 1; i <= constructorInfos.Length; i++)
{
ParameterInfo[] parameterInfos = constructorInfos[i-1].GetParameters();//GetParameters()获取指定方法或构造函数的参数
Console.WriteLine("构造函数" + i + "," + "参数数量:" + parameterInfos.Length);
foreach (var param in parameterInfos)
{
Console.WriteLine("参数名:" + param.Name + ",返回类型:" + param.ParameterType);
}
}
}
}
}
2.6 获取public成员(包含以上所有)
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
MemberInfo[] memberInfos = type.GetMembers();
foreach (var item in memberInfos)
{
Console.WriteLine("成员名称:" + item.Name + ",成员类型:" + item.MemberType);
}
}
}
}
2.6 通过枚举BindingFlags进行过滤
namespace LearnCS1
{
class MainClass
{
static void Main()
{
UserInfo userInfo = new UserInfo();
Type type = userInfo.GetType();
MemberInfo[] memberInfos = type.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var item in memberInfos)
{
Console.WriteLine("成员名称:" + item.Name + ",成员类型:" + item.MemberType);
}
}
}
}
示例说明
- GetMembers 中传入 BindingFlags 相当于是对成员信息进行一个过滤
- 除了GetMembers ,很多方法都可以传BindingFlags进行过滤
- BindingFlags 是位标志枚举,可使用 | & ^ 等运算符 | 表示取并集,& 表示取交集,^ 表示取差集
- BindingFlags.XXX | BindingFlags.Instance是固定写法,从提示信息上可以看出
2.7 获取Type 对象的三个方法
namespace LearnCS1
{
class MainClass
{
static void Main()
{
//1.typeof(类名)
Type t1 = typeof(UserInfo);
Console.WriteLine("t1.FullName:" + t1.FullName);
//2.对象.GetType()
UserInfo userInfo = new UserInfo();
Type t2 = userInfo.GetType();
Console.WriteLine("t2.FullName:" + t2.FullName);
//3.Type.GetType("命名空间.类名");
Type t3 = Type.GetType("ns_UserInfo.UserInfo");
Console.WriteLine("t3.FullName:" + t3.FullName);
}
}
}
三.加载程序集的API
namespace LearnCS1
{
class MainClass
{
static void Main()
{
//加载程序集
//方式1:将dll01.dll放在DEBUG目录下,传入无后缀名称即可加载
Assembly a1 = Assembly.Load("dll01");
//方式2:传入文件路径,会加载dll01.dll依赖的程序集
Assembly a2 = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "dll01.dll");
//方式3:传入文件路径,不会会加载dll01.dll依赖的程序集
Assembly a3 = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "dll01.dll");
Console.WriteLine(a1.FullName);
Console.WriteLine(a2.FullName);
Console.WriteLine(a3.FullName);
}
}
}
三.反射创建对象API
- 方式一:通过Assembly.CreateInstance
- 方式二:Activator.CreateInstance
- 方式三:通过调用构造函数
namespace LearnCS1
{
class MainClass
{
static void Main()
{
//step1.加载程序集
Assembly a1 = Assembly.Load("dll01");
//step2.获取所有类名
Type[] allClass = a1.GetTypes();
foreach (var oneClass in allClass)
{
//step3.根据类名创建对象
//方式一:通过Assembly.CreateInstance
object obj1 = a1.CreateInstance(oneClass.FullName);
//方式二:Activator.CreateInstance
object obj2 = Activator.CreateInstance(oneClass);
//方式三:通过调用构造函数
ConstructorInfo conInfo = oneClass.GetConstructor(new Type[] { });
object obj3 = conInfo.Invoke(new object[] { });
//step4.获取所有方法
MethodInfo[] allMethodInfo = oneClass.GetMethods();
foreach (var oneMethod in allMethodInfo)
{
//step5.调用无参方法
if (oneMethod.ReturnParameter.ParameterType.Name == "Void")
{
oneMethod.Invoke(obj1, null);
oneMethod.Invoke(obj2, null);
oneMethod.Invoke(obj3, null);
}
}
}
Console.ReadKey();
}
}
}