反射是什么
C# 编译运行过程
说到反射,就不得不说一下C#编译运行过程:
- 首先在VS点击编译的时候,就会将C#源代码编译成程序集;
程序集以可执行文件 (.exe) 或动态链接库文件(.dll)的形式实现。
- 程序集中包含有 Microsoft 中间语言(MSIL)和必需的元数据;
元数据存储以下信息:
- 程序集的说明:标识(名称、版本、区域性、公钥)、导出的类型、该程序集所依赖的其他程序集、运行所需的安全权限;
- 类型的说明:名称、可见性、基类和实现的接口、成员(方法、字段、属性、事件、嵌套的类型);
- 特性:修饰类型和成员的其他说明性元素;
- 在执行时,实时(JIT)编译器将 MSIL 转换为本机代码;
运行 Microsoft 中间语言 (MSIL) 前,必须根据公共语言运行时将其编译为目标计算机基础结构的本机代码。
- 运行代码;
公共语言运行时提供启用要发生的托管执行的基础结构以及执行期间可使用的服务。
反射与元数据
反射来自 System.Reflection命名空间,它可以读取程序集中的元数据,利用元数据创建对象,从而实现各种功能。
反射的优缺点
优点:提高了程序的灵活性和扩展性,降低耦合度;
缺点:由于反射多了一道程序,性能上相较于直接代码要慢;
反射的使用
System.Type
Type类基本属性
Type中的Assembly属性
static void Main(string[] args)
{
Type type1 = typeof(User);
//属性 Assembly
//获取声明该类型的 Assembly。
//对于泛型类型,则获取定义该泛型类型的 Assembly。
Assembly assembly = type1.Assembly;
Console.WriteLine($"{assembly.FullName}");
Console.ReadLine();
}
Type 中的 AssemblyQualifiedName 属性 和 FullName 属性
上图中类型的程序集限定名的格式中的全名称部分即是 Type 中的FullName属性值。
FullName 获取该类型的完全限定名称,包括其命名空间,但不包括程序集。
Type类常用方法
Type类获取元数据
// UserInfo类是为介绍Type类中常用方法而准备的对象
public class UserInfo
{
private int _num = 0;
public string Phone = "1311111111";
public string Name { get; set; }
public string Address { get; set; }
public UserInfo()
{
Console.WriteLine("UserInfo默认构造函数");
}
public UserInfo(string name)
{
Console.WriteLine($"UserInfo参数化构造函数:{name}");
}
public int PublicMethod()
{
return int.MinValue;
}
internal void InternalMethod ()
{
}
private void PrivateMethod()
{
}
}
class Program
{
static void Main(string[] args)
{
UserInfo userInfo = new UserInfo();
//【*】通过System.Object中的GetType()获取Type实例
Type type = userInfo.GetType();
//GetConstructors()获取所有的公共的构造函数
ConstructorInfo[] constructorInfos= type.GetConstructors();
foreach (var item in constructorInfos)
{
//GetParameters()获取指定方法或构造函数的参数
ParameterInfo[] parameterInfos = item.GetParameters();
foreach (var pi in parameterInfos)
{
Console.WriteLine($"{item.Name}:{pi.Name}:{pi.ParameterType}");
}
}
//获取当前Type 实例的所有Public方法
MethodInfo[] methodInfos = type.GetMethods();
foreach (var item in methodInfos)
{
Console.WriteLine($"{type.Name}类型中有:{item.Name}方法,返回类型为{item.ReturnType}");
}
//获取当前Type 实例的所有Public属性
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (var item in propertyInfos)
{
Console.WriteLine($"{type.Name}类中有 属性-{item.Name} 类型为-{item.PropertyType}");
}
//获取当前Type 实例的所有Public字段
FieldInfo[] fieldInfos = type.GetFields();
foreach (var item in fieldInfos)
{
Console.WriteLine($"{type.Name}类中有 字段-{item.Name} 类型为-{item.FieldType}");
}
MemberInfo[] memberInfos = type.GetMembers();
foreach (var item in memberInfos)
{
Console.WriteLine($"{type.Name}类中有 成员名称-{item.Name} 类型为-{item.MemberType}");
}
Console.ReadLine();
}
}
BindingFlags
Type type1 = Type.GetType("ConsoleApp1.UserInfo");
//GetMembers 中传入 BindingFlags 相当于是对成员信息进行一个过滤
//BindingFlags 不仅仅是GetMembers 专有,很多方法中都可以传入BindingFlags进行过滤
//BindingFlags 是位标志枚举,可使用 | & ^ 等运算符 | 表示取并集,& 表示取交集,^ 表示取差集
//BindingFlags.Public 表示公共成员
//BindingFlags.NonPublic 表示非公共成员
//BindingFlags.Instance 表示实例成员
//BindingFlags.Static 表示静态成员
MemberInfo[] memberInfos = type1.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var item in memberInfos)
{
Console.WriteLine($"BindingFlags.NonPublic|BindingFlags.Instance(实例非公共成员)名称:{item.Name}");
}
BindingFlags.Instance 和BindingFlags.Static :实例成员是相对于静态成员而言的,多数情况下我们都省略了BindingFlags 这个参数,少数需要筛选成员的时候,传入该参数。
System.Activator
//Activator类主要用于创建对象的实例
Type type = typeof(UserInfo);
UserInfo userInfo=(UserInfo)Activator.CreateInstance(type);
System.Reflection.Assembly
public void TestAssembly()
{
//通过项目名称加载程序集
var assembly = Assembly.Load("Project");
//通过dll的路径加载程序集
var loadFile = Assembly.LoadFile(@"D:\project\c#\Solution\Project\bin\Debug\net6.0\Project.dll");
}
反射调用方法
public void TestAssembly()
{
// 调用无参数的方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show1 = type.GetMethod("Show1");
show1.Invoke(oInstance, new object[] { });
show1.Invoke(oInstance, new object[0]);
show1.Invoke(oInstance, null);
// 调用有参数的方法 需要通过方法参数类型类区别方法,传递参数,严格匹配参数类型
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show2 = type.GetMethod("Show2");
show2.Invoke(oInstance, new object[] { 123 });
MethodInfo show31 = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
show31.Invoke(oInstance, new object[] { "一一一", 234 });
MethodInfo show32 = type.GetMethod("Show3", new Type[] { typeof(int) });
show32.Invoke(oInstance, new object[] { 345 });
MethodInfo show33 = type.GetMethod("Show3", new Type[] { typeof(string) });
show33.Invoke(oInstance, new object[] { "二二二" });
MethodInfo show34 = type.GetMethod("Show3", new Type[0]);
show34.Invoke(oInstance, null);
// 调用私有方法 在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oInstance, new object[] { "String" });
//调用静态方法 不需要创建对象也可以调用
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
MethodInfo show5 = type.GetMethod("Show5");
show5.Invoke(null, new object[] { "String" });
// 调用普通类的泛型方法 获取到泛型方法后需要先确定类型
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.GenericMethod");
object oInstance = Activator.CreateInstance(type);
MethodInfo show = type.GetMethod("Show");
// 获取到泛型方法后需要先确定类型
MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
genericshow.Invoke(oInstance, new object[] { 123, "三三三", DateTime.Now });
// 调用泛型类的普通方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
// 泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.GenericClass`3");
// 泛型类获取到类型后需要先确定类型
Type generType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
object oInstance = Activator.CreateInstance(generType);
MethodInfo show = generType.GetMethod("Show");
show.Invoke(oInstance, new object[] { 123, "四四四", DateTime.Now });
// 调用泛型类的泛型方法
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
// 泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.GenericDouble`1");
// 泛型类获取到类型后需要先确定类型
Type generType = type.MakeGenericType(new Type[] { typeof(int) });
object oInstance = Activator.CreateInstance(generType);
MethodInfo show = generType.GetMethod("Show");
// 获取到泛型方法后需要先确定类型
MethodInfo genericMethod = show.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) });
genericMethod.Invoke(oInstance, new object[] { 123, "五五五", DateTime.Now });
}
反射方式取值赋值
//反射方式赋值取值
Console.WriteLine("***********反射方式赋值取值*************");
Type type = typeof(People);
object pObject = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{
if (prop.Name.Equals("Id"))
{
prop.SetValue(pObject, 134);
}
else if (prop.Name.Equals("Name"))
{
prop.SetValue(pObject, "WWWW");
}
else if (prop.Name.Equals("Age"))
{
prop.SetValue(pObject, 25);
}
}
foreach (var prop in type.GetProperties())
{
Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");
}
反射创建对象的方式
public class MyClass
{
public MyClass()
{
Console.WriteLine("MyClass 的无参构造函数被调用");
}
}
class Program
{
static void Main(string[] args)
{
// 方式1: 使用 Activator.CreateInstance
object obj1 = Activator.CreateInstance(typeof(MyClass));
// 方式2: 使用 Assembly.CreateInstance
Assembly assembly = Assembly.GetExecutingAssembly();
object obj2 = assembly.CreateInstance("ConsoleApp1.MyClass"); // 注意指定完全限定名
// 方式3: 使用 ConstructorInfo.Invoke
Type type = typeof(MyClass);
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
object obj3 = constructor.Invoke(null);
}
}