反射 System.Reflection
反射 System.Reflection
基本概念
- 元数据 metadata:有关程序及其类型的数据称为元数据,保存于程序集中;
- 反射 reflection:一个运行的程序查看本身或其他程序的元数据的行为称为反射;
- 特性 attribute:允许向程序集增加元数据的语言结构,用于保存某种特殊类型的类;
Type 类
一种抽象类,所有类型的基类;
对于程序中使用的每一个类型,CLR都会实例化Type类型的对象;
不管类型有多少个实例,只有一个Type类对象会关联到所有这些实例;
从type对象中可以获取几乎所有有关类型的信息
通过 GetType方法和 typeof 运算符获取 Type 对象
特性 attribute
特征是一种允许我们向程序的程序集增加元数据的语言结构;
用于保存程序结构信息的某种特殊类型的类;
Pascal 命名法并且采用 Attribute 后缀;
大多数特征只针对直接跟随在一个或多个特征片段后的结构;
可以为单个结构使用多个特性,两种声明方法
[ serializeble] [ MyAttribute(“Simple Class”,”Version 3.57”)] //多层结构 [MyAttribute(“Simple Class”,”Version 3.57”), serializeble] //逗号分隔
除了类,特性还可以被应用到字段、属性等其他程序结构
可以显示的标注特性
[method:MyAttribute(“Simpl Method”,”Version 3.57”)] [return: MyAttribute(“This value represents …”,”Version 2.3”)] public long ReturnSetting(){…}
C# 语言定义的10个标准的特性目标
- event、method、property、type、assembly、field、param、return、typevar、module
可以通过使用assembly和medule目标名称来使用显示目标说明符把特性设置在程序集或模块级别;
程序集级别的特性必须放在任何命名空间之外,一般放在AssemblyInfo.cs文件中,该文件通常包含有关公司、产品;
NET 预定义特性
Obsolete
将旧代码标记为过期;
// 使用该特征修饰的结构时将标记为警告 [ Obsolete ( “ Message string “ ) ] // 使用该特征修饰的结构时将标记为错误 [ Obsolete ( “ Message string “ , true ) ]
Conditional
允许我们包括或排除特定方法的所有调用;
如果定义了编译符合,那么编译器会包含所有调用此方法的代码,否则将会忽略代码中这个方法的所有调用;
// 如果没有”DoTrace”,编译器将不会对该特征的目标进行编译 [ Conditional (“ DoTrace “)]
CallerFilePath、CallerLineNumber、CallerMemberName
用于访问文件路径、代码行数、调用成员的名称等源代码信息;
这些特性只能用于方法中的可选参数;
public static void MyTrace ( string message, [CallerFilePath] string fileName="", [CallerLineNumber] int LineNumber=0, [CallerMemberName] string callingMember=””);
DebuggerStepThrough
- 告诉调试器在执行目标代码时不进入该方法,只执行该方法;
- 该特性位于System.Diagnostics命名空间;
- 该特性可用于类、结构、构造函数、方法或访问器;
CLSComliant
- 声明可公开的成员应该被编译器检测是否符合CLS,兼容的程序集可以被任何.NET兼容的语言使用
Serializable
- 声明结构可以被序列化
NonSerialized
- 声明结构不能被序列号
DLLImport
- 声明是非托管代码实现的
WebMethod
- 声明方法应该被作为XML Web服务的一部分暴露
AttributeUsage
- 声明特性能应用到什么类型的程序结构,将这个特性应用到特性声明上
- 公共属性
- ValidOn:保存特性能应用到的目标类型列表,构造函数的第一个参数必须是AttributeTarget类型的枚举值
- Inherted:布尔值,指示特性是否会被装饰类型的派生类所继承,默认 true
- AllowMutiple:布尔值,指示目标是否被应用多个特性的实例,默认 false
- AttributeTarget 枚举成员:All、Assembly、Class、Constructor、Delegate、Enum、Event、Filld、GenericParameter、Interface、Method、Module、Parameter、Property、ReturnValue、Struct
自定义特性——派生自 System.Attribute
声明
通常以Attribute后缀,并且为sealed修饰
public sealed class MyAttributeAttribute : System.Attribute { … }
公共成员只能是字段、属性、构造函数;
特性类应该表示目标结构的一些状态;
在特性声明中使用AttributeUsage来显示指定特性目标组;
自定义特性属于特殊的类;
访问特性
IsDefined 方法:使用Type对象的IsDefined方法检测某个特性是否应用到了某个类上
MyClass mc = new NyClass (); Type t = mc.GetType(); bool isDefined = t.IsDefined ( typeof ( AttributeName ), false) //fasle指示是否搜索MyClass的继承树
GetCustomAttributes 方法:返回应用到结构的特性的数组;调用该方法后,每一个与目标关联的特性的实例就会被创建
object [] AttArr = t.GetCustomAttributes ( false );
示例:自定义单元测试
主函数运行所有测试实例
// 定义服务,完成注册 ServiceCollection services = new ServiceCollection(); services.AddMemoryCache(); services.AddBDCloudDrive(); // 注册程序集下的所有实现 TestControllerBase 的类 Assembly.GetExecutingAssembly().GetTypes().Where(type => typeof(TestControllerBase).IsAssignableFrom(type) && type != typeof(TestControllerBase)) .ToList().ForEach(e => services.AddScoped(typeof(ITestController), e)); // 调用测试类 using ServiceProvider provider = services.BuildServiceProvider(); provider.GetRequiredService<ITestController>().Excute();
定义测试类接口,方便主程序自动注册
internal interface ITestController { public void Excute(); }
定义测试方法特性
[AttributeUsage(AttributeTargets.Method)] internal sealed class TestMethodAttribute:Attribute {}
创建通用测试基类,重写 Excute 方法
internal class TestControllerBase : ITestController { public void Excute() { var tasks = new List<Task>(); // 获取所有需要测试的方法 var methods = GetType().GetMethods() .Where(e => e.IsDefined(typeof(TestMethodAttribute), true) && e.IsPublic && !e.ContainsGenericParameters); // 调用所有需要测试的方法 foreach (var m in methods) { var t = m.Invoke(this, null); // 如果是异步方法需要统一等待 if (t != null && t is Task) tasks.Add((Task)t); } Task.WaitAll(tasks.ToArray()); } }
编写测试类,继承通用的测试基类
// 实现 ITestController internal class TestBDCloudDriveProvider : TestControllerBase { private readonly ICloudDriveProvider cloudDrive; // 注入需要测试的服务 public TestBDCloudDriveProvider(ICloudDriveProvider provider) { cloudDrive = provider; } // 添加 [TestMethod] 特性,表示该方法需要测试 [TestMethod] public async Task TestDownloadAsync() { /** 测试代码 **/ } }