1. 概述
一个.net程序不仅包含代码和数据,还包含 元数据。
本章介绍如何应用attributes以及如何使用反射来获取它,还有如何使用CodeDom和expression trees来实现在运行时生成代码。
2. 主要内容
2.1 创建和使用attributes
① attributes用来向程序添加元数据。可以应用到所有类型:程序集、类、方法、参数、属性。
[Conditional("CONDITION1"), Conditional("CONDITION2")] static void MyMethod() { }
② AssemblyInfo.cs中保存的是应用到当前程序集的所有attributes。
③ 可以使用attributes的IsDefined和GetCustomAttribute方法读取attributes
if (Attribute.IsDefined(typeof(Person), typeof(SerializableAttribute))) { } ConditionalAttribute conditionalAttribute = (ConditionalAttribute)Attribute.GetCustomAttribute( typeof(ConditionalClass), typeof(ConditionalAttribute)); string condition = conditionalAttribute.ConditionString; //CONDITION1
④ 可以自定义attributes
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple=true)] class CompleteCustomAttribute : Attribute { public CompleteCustomAttribute(string description) { Description = description; } public string Description {get; set; } }
2.2 使用反射
反射 使得一个应用程序可以收集自身的信息并使用。反射的运行速度比其他静态代码慢。
public interface IPlugin { string Name { get; } string Description {get; } bool Load(MyApplication application); } Assembly pluginAssembly = Assembly.Load("assemblyName"); var plugins = from type in pluginAssembly.GetTypes() where typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface select type; foreach(Type pluginType in plugins) IPlugin plugin = Activitor.CreateInstance(pluginType) as IPlugin;
使用反射还可以获取属性的值和执行指定的方法。
int i = 42; MethodInfo compareToMethod = i.GetType().GetMethod("CompareTo" , new Type[] { typeof(int) }; int result = (int)compareToMethod.Invoke(i, new object[] { 41 });
2.3 使用CodeDom和Lambda表达式生成代码
① 使用CodeDom
CodeCompileUnit compileUnit = new CodeCompileUnit(); CodeNamespace myNamespace = new CodeNamespace("MyNamespace"); myNamespace.Imports.Add(new CodeNamespaceImport("System")); CodeTypeDeclaration myClass = new CodeTypeDeclaration("MyClass"); CodeEntryPointMethod start = new CodeEntryPointMethod(); CodeMethodInvokeExpression cs1 = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("Console"), "WriteLine", new CodePrimitiveExpression("Hello World!")); compileUnit.Namespaces.Add(myNamespace); myNamespace.Types.Add(myClass); myClass.Members.Add(start); start.Statements.Add(cs1);
编译单元定义完成后,使用CSharpCodeProvider来执行生成。
CSharpCodeProvider provider = new CSharpCodeProvider(); using(StreamWriter sw = new StreamWriter("HelloWorld.cs", false)) { IndentedTextWriter tw = new IndentedTypeWriter(sw, " "); provider.GenerateCodeFromCompileUnit(compileUnit, tw, new CodeGeneratorOptions()); tw.Close(); }
② 使用 expression trees 实现上述功能
BlockExpression blockExpr = Expression.Block( Expression.Call( null, typeof(Console).GetMethod("Write", new Type[] { typeof(String)}), Expression.Constant("Hello ") ), Expression.Call( null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String)}), Expression.Constant("World!") ), ); Expression.Lambda<Action>(blockExpr).Compile();
3. 总结
① 一个C#程序集包括代码和元数据。
② Attributes是元数据的一种,可以被应用到代码中,可以在运行时被查询到。
③ 反射是一个在C#程序中检查元数据的过程。
④ 利用反射,可以 创建类型、调用方法、读取属性 等等。
⑤ CodeDom用于在运行时创建一个编译单元。可以被编译或转化成源码文件。
⑥ 表达式树 描述一块代码,可以被翻译成其他语言(比如sql),也可以被编译和执行。