[C# Essentials] Reflection

Tags
元数据:程序集中每个类型的相关数据,包括其继承关系与成员,编译器会将这些数据提取并嵌入到程序集里。
通过反射机制可以在运行时动态获取元数据
C#→CIL时,它会维持大部分元数据,可以利用反射机制枚举这些元数据
透过System.Type的实例来访问类型的元数据
 

System.Type

APIs

notion image
object obj = Activator.CreateInstance(Type type) 创建一个类实例

object.GetType()

返回原始对象的运行时Type实例
静态类无法实例化,所以无法调用GetType()
 

typeof

操作符
返回编译时(CLR2.0后支持泛型)绑定的Type实例,可以作用于一个类
 
 

成员调用

PropertyInfo和MethodInfo
using System; using System.Reflection; namespace TestUse; public class Reflection { public class Person { public string Name { get; set; } public int Age { get; private set; } public Person(string name, int age) { Name = name; Age = age; } } public class Calculator { public int Add(int a, int b) { return a + b; } public static int Multiply(int a, int b) { return a * b; } } static void Main() { Person person = new Person("Alice", 25); Person person2 = new Person("AliceIsu", 27); // 获取 Person 类型的 Name 属性 PropertyInfo nameProperty = typeof(Person).GetProperty("Name"); // 获取属性值 string nameValue = (string)nameProperty.GetValue(person); Console.WriteLine($"Name: {nameValue}"); // 设置属性值 nameProperty.SetValue(person, "Bob"); Console.WriteLine($"Updated Name: {person.Name}"); // 获取 Age 属性 PropertyInfo ageProperty = typeof(Person).GetProperty("Age"); Console.WriteLine($"Age PropertyType: {ageProperty.PropertyType.Name}"); // 输出: Int32 Console.WriteLine($"CanWrite: {ageProperty.CanWrite}"); // 输出: False (因为 Age 的 set 是私有的) Calculator calculator = new Calculator(); // 获取 Add 方法 MethodInfo addMethod = typeof(Calculator).GetMethod("Add"); // 调用 Add 方法 object result = addMethod.Invoke(calculator, new object[] { 3, 5 }); Console.WriteLine($"Add Result: {result}"); // 输出: 8 // 获取 Multiply 方法(静态方法) MethodInfo multiplyMethod = typeof(Calculator).GetMethod("Multiply"); // 调用静态方法(实例参数传 null) object staticResult = multiplyMethod.Invoke(null, new object[] { 4, 6 }); Console.WriteLine($"Multiply Result: {staticResult}"); // 输出: 24 // 获取方法参数信息 ParameterInfo[] parameters = addMethod.GetParameters(); foreach (var param in parameters) { Console.WriteLine($"Parameter: {param.Name}, Type: {param.ParameterType.Name}"); } } }
二者都间接继承自MemberInfo类
 

Attribute

特性可以为类、方法、属性或者程序集的元数据中加上一点东西
自定义特性:继承Attribute类
 
查找特性
GetCustomAttributes方法:获得某类的所有(对应类型的特性)
inherit表示是否要搜索该类的继承链上的类的特性
获取Attribute对象之后就可以访问其中的属性了
Prototype:
object[] PropertyInfo.GetCustomAttributes(Type attributeType, bool inherit) T[] ProperInfo.GetCustomAttributes<T>(bool inherit) where T : Attribute
 
构造函数初始化特性
Attribute的构造函数会在给其他东西加特性的时候用到
[MyDescription("MyClass", "This is a class")] public class MyClass { [MyDescription("MyMethod", "This is a method")] public void MyMethod() { } public void MyMethodWithoutAttribute() {} } public class MyDescriptionAttribute : Attribute { public MyDescriptionAttribute(params string[] info) { StringBuilder sb = new StringBuilder(); foreach (var str in info) { sb.AppendLine(str); } Description = sb.ToString(); } public string Description; public override string ToString() { return Description; } } MyClass obj = new MyClass(); Type myClassType = obj.GetType(); MyDescriptionAttribute? classAttr = myClassType.GetCustomAttribute<MyDescriptionAttribute>(false); Console.WriteLine(classAttr?.ToString() ?? $"No such attribute on class: {myClassType.Name}"); MethodInfo[] myClassMethodInfos = myClassType.GetMethods(); foreach (var methodInfo in myClassMethodInfos) { MyDescriptionAttribute? methodAttr = methodInfo.GetCustomAttribute<MyDescriptionAttribute>(false); Console.WriteLine(methodAttr?.ToString() ?? $"No such attribute on method: {methodInfo.Name}"); } /* Results */ /* MyClass This is a class MyMethod This is a method No such attribute on method: MyMethodWithoutAttribute No such attribute on method: GetType No such attribute on method: ToString No such attribute on method: Equals No such attribute on method: GetHashCode */
 
System.AttributeUsageAttribute
修饰自定义特性,限定这个特性的使用范围
[AttributeUsage(AttributeUsages.Property | AttributeUsages.Field)] public class MyAttribute : Attribute { }
这是预定义特性,该特性不存在运行时代码,由编译器内建对它的支持
 
System.ConditionalAttribute(string condition)
用于检查当前位置是否有定义condition宏
修饰方法,若未定义这个宏则为这个调用赋予no-op行为(即,让这个方法的调用变成一条no-op)
 

泛型支持

Type.ContainsGenericParameters 判断类和方法是否包含未设置的泛型参数
Type.IsGenericType 判断类型是否泛型
System.Type[] GetGenericArguments() 返回泛型类的类型参数被声明的顺序
 

动态对象 (C# 4.0)

dynamic对象
dynamic是通知编译器生成代码的指令
值类型 与 dynamic类型的相互转换会涉及装拆箱
调用dynamic对象的方法不会在编译时检查,而是在运行时执行到这个调用操作时才会检查这个方法调用的合理性
获取dynamic对象的成员也会返回一个dynamic对象
成员调用不合法时,抛出Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
 
无法调用dynamic对象(在此时对应的实现类型)的扩展方法,只能在实现类型上调用
 
Internal机制:
dynamic类型实际是一个System.Object
调用成员时,编译器需要声明比较复杂的东西,看书
执行时,用反射查找成员并验证签名是否匹配,然后生成一个表达式树;这个表达式树编译出来的CIL在调用点被缓存,通过一个委托触发调用
由于CIL已被缓存,后续调用不会再产生反射和编译的开销
 

动态编程 V.S. 静态编程

(不是很懂)
用动态编程的好处是,对于COM对象(如XML, 数据库表)的动态数据的访问,使用静态编程时类型安全性没什么作用,写起来还更复杂
举个例子,DynamicXml.Parse(…) 根据XML内容动态绑定到返回对象的值上,而非反射
notion image
 
 

自定义动态对象

实现System.Dynamic.IDynamicMetaObjectProvider 接口
一般实现System.Dynamic.DynamicObject 即可
重写System.Dynamic.DynamicObject的虚方法即可