第十章 管理类型(In .net4.5) 之 使用反射

1. 概述

  一个.net程序不仅包含代码和数据,还包含 元数据

  本章介绍如何应用attributes以及如何使用反射来获取它,还有如何使用CodeDomexpression 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),也可以被编译和执行。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。