相关文章推荐
卖萌的眼镜  ·  node.js - ...·  1 年前    · 
开朗的眼镜  ·  一种改进的MobileNet- ...·  1 年前    · 
飞奔的机器猫  ·  图像库 libpng ...·  1 年前    · 
谦逊的针织衫  ·  Bootstrap之表单 - ...·  1 年前    · 

反射提供了描述程序集、模块和类型的对象( Type 类型)。 可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。 有关更多信息,请参见 利用特性扩展元数据

下面是使用静态方法 GetType (从 Object 基类派生的所有类型都继承该方法)获取变量类型的简单反射示例:

// Using Reflection to get information from an Assembly:
System.Reflection.Assembly info = typeof(System.Int32).Assembly;
System.Console.WriteLine(info);
      

通过 System.Reflection 命名空间中的类以及 System.Type,您可以获取有关已加载的程序集和在其中定义的类型(如接口值类型)的信息。 您也可以使用反射在运行时创建类型实例,以及调用和访问这些实例。 有关反射的特定方面的主题,请参见本概述末尾的相关主题

公共语言运行时加载器管理应用程序域,这些域在拥有相同应用程序范围的对象周围形成了确定边界。 这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。

程序集包含模块,而模块包含类型,类型又包含成员。 反射则提供了封装程序集、模块和类型的对象。 您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。 然后,可以调用类型的方法或访问其字段和属性。 反射通常具有以下用途:

  • 使用 Assembly 定义和加载程序集,加载在程序集清单中列出的模块,以及从此程序集中查找类型并创建该类型的实例。

  • 使用 Module 发现以下信息:包含模块的程序集以及模块中的类等。 您还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

  • 使用 ConstructorInfo 发现以下信息:构造函数的名称、参数、访问修饰符(如 publicprivate)和实现详细信息(如 abstractvirtual)等。 使用 TypeGetConstructorsGetConstructor 方法来调用特定的构造函数。

  • 使用 MethodInfo 发现以下信息:方法的名称、返回类型、参数、访问修饰符(如 publicprivate)和实现详细信息(如 abstractvirtual)等。 使用 TypeGetMethodsGetMethod 方法来调用特定的方法。

  • 使用 FieldInfo 发现以下信息:字段的名称、访问修饰符(如 publicprivate)和实现详细信息(如 static)等;并获取或设置字段值。

  • 使用 EventInfo 发现以下信息:事件的名称、事件处理程序数据类型、自定义特性、声明类型和反射类型等;并添加或移除事件处理程序。

  • 使用 PropertyInfo 发现以下信息:属性的名称、数据类型、声明类型、反射类型和只读或可写状态等;并获取或设置属性值。

  • 使用 ParameterInfo 发现以下信息:参数的名称、数据类型、参数是输入参数还是输出参数,以及参数在方法签名中的位置等。

  • 当您在一个应用程序域的仅反射上下文中工作时,请使用 CustomAttributeData 来发现有关自定义特性的信息。 通过使用 CustomAttributeData,您不必创建特性的实例就可以检查它们。

System.Reflection.Emit 命名空间的类提供了一种特殊形式的反射,使您能够在运行时生成类型。

反射也可用于创建称作类型浏览器的应用程序,它使用户能够选择类型,然后查看有关选定类型的信息。

反射还有其他一些用途。 JScript 等语言编译器使用反射来构造符号表。 System.Runtime.Serialization 命名空间中的类使用反射来访问数据并确定要持久保存的字段。 System.Runtime.Remoting 命名空间中的类通过序列化来间接地使用反射。

反射中的运行时类型

反射提供类(例如 TypeMethodInfo)来表示类型、成员、参数和其他代码实体。 但是,在您使用反射时,您并不直接使用这些类,这些类中的大多数是抽象的(在 Visual Basic 中为 MustInherit)。 您使用的是公共语言运行时 (CLR) 提供的类型。

例如,使用 C# 的 typeof 运算符(在 Visual Basic 中为 GetType)获取 Type 对象时,该对象实际上是 RuntimeType RuntimeType 派生自 Type,并提供所有抽象方法的实现。

这些运行时类是 internal(在 Visual Basic 中为 Friend)。 它们的文档与它们的基类的文档并没有分开,因为它们的行为是由基类文档描述的。

System.Type 类对于反射起着核心的作用。 当反射请求加载的类型时,公共语言运行时将为它创建一个 Type 您可以使用 Type 对象的方法、字段、属性和嵌套类来查找有关该类型的所有信息。

使用 Assembly.GetTypeAssembly.GetTypes 从尚未加载的程序集中获取 Type 对象,并传入所需类型的名称。 使用 Type.GetType 可从已加载的程序集中获取 Type 对象。 使用 Module.GetTypeModule.GetTypes 可获取模块 Type 对象。

注意

如果想要检查和操作泛型类型和方法,请参见反射类型和泛型类型如何:使用反射检查和实例化泛型类型中提供的附加信息。

下面的示例显示在获取程序集的 Assembly 对象和模块时所必需的语法。

// Loads an assembly using its file name.
Assembly a = Assembly.LoadFrom("MyExe.exe");
// Gets the type names from the assembly.
Type[] types2 = a.GetTypes();
foreach (Type t in types2)
    Console.WriteLine(t.FullName);
      

在获取了一个 Type 后,您可以采用许多方法来发现与该类型的成员有关的信息。 例如,通过调用 Type.GetMembers 方法(该方法将获取对当前类型的每个成员进行描述的一组 MemberInfo 对象),您可以找到有关该类型的所有成员的信息。

您也可以在 Type 类上使用方法,以检索有关按名称指定的一个或多个构造函数、方法、事件、字段或属性的信息。 例如,Type.GetConstructor 封装当前类的特定构造函数。

如果具有 Type,则可以使用 Type.Module 属性来获取一个封装该类型所在模块的对象。 使用 Module.Assembly 属性可查找封装模块所在程序集的对象。 使用 Type.Assembly 属性可直接获取封装该类型的程序集。

System.Type 和 ConstructorInfo

下面的示例演示如何列出一个类(此示例中为 String 类)的构造函数。

// This program lists all the public constructors
// of the System.String class.
using System;
using System.Reflection;
class ListMembers
    public static void Main()
        Type t = typeof(System.String);
        Console.WriteLine("Listing all the public constructors of the {0} type", t);
        // Constructors.
        ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
        Console.WriteLine("//Constructors");
        PrintMembers(ci);
    public static void PrintMembers(MemberInfo[] ms)
        foreach (MemberInfo m in ms)
            Console.WriteLine("{0}{1}", "     ", m);
        Console.WriteLine();
        Console.WriteLine ("\nReflection.MemberInfo");
        // Gets the Type and MemberInfo.
        Type MyType = Type.GetType("System.IO.File");
        MemberInfo[] Mymemberinfoarray = MyType.GetMembers();
        // Gets and displays the DeclaringType method.
        Console.WriteLine("\nThere are {0} members in {1}.",
            Mymemberinfoarray.Length, MyType.FullName);
        Console.WriteLine("{0}.", MyType.FullName);
        if (MyType.IsPublic)
            Console.WriteLine("{0} is public.", MyType.FullName);
            
// This code displays information about the GetValue method of FieldInfo.
using System;
using System.Reflection;
class MyMethodInfo
    public static int Main()
        Console.WriteLine("Reflection.MethodInfo");
        // Gets and displays the Type.
        Type MyType = Type.GetType("System.Reflection.FieldInfo");
        // Specifies the member for which you want type information.
        MethodInfo Mymethodinfo = MyType.GetMethod("GetValue");
        Console.WriteLine(MyType.FullName + "." + Mymethodinfo.Name);
        // Gets and displays the MemberType property.
        MemberTypes Mymembertypes = Mymethodinfo.MemberType;
        if (MemberTypes.Constructor == Mymembertypes)
            Console.WriteLine("MemberType is of type All");
        else if (MemberTypes.Custom == Mymembertypes)
            Console.WriteLine("MemberType is of type Custom");
        else if (MemberTypes.Event == Mymembertypes)
            Console.WriteLine("MemberType is of type Event");
        else if (MemberTypes.Field == Mymembertypes)
            Console.WriteLine("MemberType is of type Field");
        else if (MemberTypes.Method == Mymembertypes)
            Console.WriteLine("MemberType is of type Method");
        else if (MemberTypes.Property == Mymembertypes)
            Console.WriteLine("MemberType is of type Property");
        else if (MemberTypes.TypeInfo == Mymembertypes)
            Console.WriteLine("MemberType is of type TypeInfo");
        return 0;
            
// This program lists all the members of the
// System.IO.BufferedStream class.
using System;
using System.IO;
using System.Reflection;
class ListMembers
    public static void Main()
        // Specifies the class.
        Type t = typeof(System.IO.BufferedStream);
        Console.WriteLine("Listing all the members (public and non public) of the {0} type", t);
        // Lists static fields first.
        FieldInfo[] fi = t.GetFields(BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.Public);
        Console.WriteLine("// Static Fields");
        PrintMembers(fi);
        // Static properties.
        PropertyInfo[] pi = t.GetProperties(BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.Public);
        Console.WriteLine("// Static Properties");
        PrintMembers(pi);
        // Static events.
        EventInfo[] ei = t.GetEvents(BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.Public);
        Console.WriteLine("// Static Events");
        PrintMembers(ei);
        // Static methods.
        MethodInfo[] mi = t.GetMethods (BindingFlags.Static |
            BindingFlags.NonPublic | BindingFlags.Public);
        Console.WriteLine("// Static Methods");
        PrintMembers(mi);
        // Constructors.
        ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Instance |
            BindingFlags.NonPublic | BindingFlags.Public);
        Console.WriteLine("// Constructors");
        PrintMembers(ci);
        // Instance fields.
        fi = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic |
            BindingFlags.Public);
        Console.WriteLine("// Instance Fields");
        PrintMembers(fi);
        // Instance properites.
        pi = t.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic |
            BindingFlags.Public);
        Console.WriteLine ("// Instance Properties");
        PrintMembers(pi);
        // Instance events.
        ei = t.GetEvents(BindingFlags.Instance | BindingFlags.NonPublic |
            BindingFlags.Public);
        Console.WriteLine("// Instance Events");
        PrintMembers(ei);
        // Instance methods.
        mi = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic
            | BindingFlags.Public);
        Console.WriteLine("// Instance Methods");
        PrintMembers(mi);
        Console.WriteLine("\r\nPress ENTER to exit.");
        Console.Read();
    public static void PrintMembers (MemberInfo [] ms)
        foreach (MemberInfo m in ms)
            Console.WriteLine ("{0}{1}", "     ", m);
        Console.WriteLine();
      

从反射的角度来说,泛型类型和普通类型的区别在于泛型类型与一组类型参数(如果是泛型类型定义)或类型变量(如果是构造的类型)关联。 泛型方法与普通方法的区别也在于此。

要理解反射处理泛型类型和泛型方法的方式,有两点很重要:

  • 泛型类型定义和泛型方法定义的类型参数是由 Type 类的实例表示的。

    注意

    如果 Type 对象表示泛型类型参数,则 Type 的许多属性和方法具有不同的行为。 这些不同在属性和方法主题中进行介绍。 有关示例,请参见 IsAutoClassDeclaringType 此外,某些成员仅当 Type 对象表示泛型类型参数时才有效。 有关示例,请参见 GetGenericTypeDefinition

  • 如果 Type 的实例表示泛型类型,则会包含一组表示类型参数(泛型类型定义)或类型变量(构造类型)的类型。 表示泛型方法的 MethodInfo 类的实例也是如此。

反射提供 TypeMethodInfo 方法,允许访问类型参数数组,还可以确定 Type 实例表示的是类型参数还是实际类型。

有关演示此处所讨论的方法的代码示例,请参见如何:使用反射检查和实例化泛型类型

下面的讨论假定已熟悉了泛型术语,如类型参数、类型变量以及开放式或封闭式构造类型之间的差异。 有关更多信息,请参见 .NET Framework 中的泛型

是泛型类型还是泛型方法?

使用反射检查 Type 实例表示的未知类型时,请使用 IsGenericType 属性确定未知类型是否为泛型。 如果是泛型类型,则返回 true 类似地,检查 MethodInfo 类的实例表示的未知方法时,请使用 IsGenericMethod 属性确定方法是否为泛型。

是泛型类型定义还是泛型方法定义?

使用 IsGenericTypeDefinition 属性确定 Type 对象是否表示泛型类型定义,使用 IsGenericMethodDefinition 方法确定 MethodInfo 是否表示泛型方法定义。

泛型类型定义和泛型方法定义是一些模板,根据这些模板,可以创建可实例化的类型。 .NET Framework 类库中的泛型类型(如 Dictionary<TKey, TValue>)是泛型类型定义。

类型或方法是开放式的还是封闭式的?

如果可实例化类型的所有类型参数都已替换,包括所有封闭类型的所有类型参数,则泛型类型或泛型方法是封闭式的。 如果是封闭式的,则只能创建泛型类型的一个实例。 如果类型是开放式的,则 Type.ContainsGenericParameters 属性返回 true 对于方法,MethodInfo.ContainsGenericParameters 方法执行同样的功能。

生成封闭式泛型类型

只要具有泛型类型定义或泛型方法定义,即可使用 MakeGenericType 方法创建封闭式泛型类型,或使用 MakeGenericMethod 方法为封闭式泛型方法创建 MethodInfo

获取泛型类型定义或泛型方法定义

如果具有的开放式泛型类型或方法不是泛型类型定义或泛型方法定义,则不能创建它的实例,也不能提供缺少的类型参数。 必须具有泛型类型定义或泛型方法定义。 使用 GetGenericTypeDefinition 方法可获取泛型类型定义,使用 GetGenericMethodDefinition 方法可获取泛型方法定义。

例如,如果有表示 Dictionary<int, string>(在 Visual Basic 中为 Dictionary(Of Integer, String))的 Type 对象,并且希望创建 Dictionary<string, MyClass> 类型,则可以使用 GetGenericTypeDefinition 方法获取表示 Dictionary<TKey, TValue>Type,然后使用 MakeGenericType 方法生成表示 Dictionary<int, MyClass>Type

有关不是泛型类型的开放式泛型类型的示例,请参见本主题后面部分的“是类型参数还是类型变量”。

检查类型变量和类型参数

使用 Type.GetGenericArguments 方法获取一组 Type 对象,这些对象表示泛型类型的类型参数或类型变量,对于泛型方法,使用 MethodInfo.GetGenericArguments 方法执行同样的操作。

只要了解 Type 对象表示类型参数,就可以回答许多其他问题。 可以确定类型参数的源、位置及其约束。

是类型参数还是类型变量

若要确定数组的特定元素是类型参数还是类型变量,请使用 IsGenericParameter 属性。 如果元素是类型参数,则 IsGenericParameter 属性为 true

泛型类型可以是开放式的非泛型类型定义,在这种情况下,它同时具有类型变量和类型参数。 例如,在下面的代码中,派生出类 D 的类型是通过用 D 的第一个类型参数替换 B 的第二个类型参数创建的。

泛型类型参数可能来自要检查的类型、封闭式类型或泛型方法。 可以确定泛型类型参数的源,方法如下:

  • 首先,使用 DeclaringMethod 属性确定类型参数是否来自泛型方法。 如果该属性值不是空引用(在 Visual Basic 中为 Nothing),则源是泛型方法。

  • 如果源不是泛型方法,则使用 DeclaringType 属性确定泛型类型参数所属的泛型类型。

如果类型参数属于泛型方法,则 DeclaringType 属性返回声明该泛型方法的类型,这已没有意义。

泛型参数的位置

在极少情况下,需要确定类型参数在其声明类的类型参数列表中的位置。 例如,假定具有一个 Type 对象,该对象表示上一示例中的 B<int, V> 类型。 GetGenericArguments 方法提供类型变量列表,在检查 V 时,可以使用 DeclaringMethodDeclaringType 属性获取其来源。 然后,可以使用 GenericParameterPosition 属性确定其在定义时在类型参数列表中的位置。 在本示例中,V 在进行定义的类型参数列表的位置 0(零)处。

基类型约束和接口约束

使用 GetGenericParameterConstraints 方法可获取类型参数的基类型约束和接口约束。 数组元素的顺序不重要。 如果是接口类型,则元素表示接口约束。

泛型参数特性

GenericParameterAttributes 属性获取 GenericParameterAttributes 值,此值指示类型参数的变体(协变或逆变)和特殊约束。

协变和逆变

若要确定类型参数是协变还是逆变,请将 GenericParameterAttributes.VarianceMask 掩码应用于由 GenericParameterAttributes 属性返回的 GenericParameterAttributes 值。 如果结果为 GenericParameterAttributes.None,则类型参数为固定参数。 请参见泛型中的协变和逆变

若要确定类型参数的特殊约束,请将 GenericParameterAttributes.SpecialConstraintMask 掩码应用于由 GenericParameterAttributes 属性返回的 GenericParameterAttributes 值。 如果结果为 GenericParameterAttributes.None,则没有任何特殊约束。 可以将类型参数约束为引用类型、不可为 null 的值类型和具有默认构造函数。

与其他类型的信息一样,泛型类型的信息的获取方式为:检查表示泛型类型的 Type 对象。 主要的差异在于,泛型类型具有一组表示其泛型类型参数的 Type 对象。 本部分的第一个步骤是检查泛型类型。

通过将类型变量绑定到泛型类型定义的类型参数,可以创建表示构造类型的 Type 对象。 第二个步骤演示此过程。

检查泛型类型及其类型参数

  1. 获取表示泛型类型的 Type 实例。 在下面的代码中,使用 C# 的 typeof 运算符(在 Visual Basic 中为 GetType,在 Visual C++ 中为 typeid)获取类型。 有关获取 Type 对象的其他方法,请参见 Type 类主题。 注意,余下的步骤中,类型包含在名为 t 的方法参数中。

    Console.WriteLine("   Is this a generic type? {0}",
        t.IsGenericType);
    Console.WriteLine("   Is this a generic type definition? {0}",
        t.IsGenericTypeDefinition);
                 
    Console.WriteLine("   List {0} type arguments:", 
        typeParameters.Length);
    foreach( Type tParam in typeParameters )
        if (tParam.IsGenericParameter)
            DisplayGenericParameter(tParam);
            Console.WriteLine("      Type argument: {0}",
                tParam);
                 
    private static void DisplayGenericParameter(Type tp)
        Console.WriteLine("      Type parameter: {0} position {1}", 
            tp.Name, tp.GenericParameterPosition);
                 
    Type classConstraint = null;
    foreach(Type iConstraint in tp.GetGenericParameterConstraints())
        if (iConstraint.IsInterface)
            Console.WriteLine("         Interface constraint: {0}",
                iConstraint);
    if (classConstraint != null)
        Console.WriteLine("         Base type constraint: {0}", 
            tp.BaseType);
        Console.WriteLine("         Base type constraint: None"); 
                 
    if (sConstraints == GenericParameterAttributes.None)
        Console.WriteLine("         No special constraints.");
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.DefaultConstructorConstraint))
            Console.WriteLine("         Must have a parameterless constructor.");
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.ReferenceTypeConstraint))
            Console.WriteLine("         Must be a reference type.");
        if (GenericParameterAttributes.None != (sConstraints &
            GenericParameterAttributes.NotNullableValueTypeConstraint))
            Console.WriteLine("         Must be a non-nullable value type.");
          

    构造泛型类型的实例

    泛型类型和模板类似。 除非指定其泛型类型参数的实际类型,否则不能创建泛型类型的实例。 若要在运行时使用反射创建实例,需要使用 MakeGenericType 方法。

    1. 获取表示泛型类型的 Type 对象。 下面的代码以两种不同方式获取泛型类型 Dictionary<TKey, TValue>:一种方法使用 Type.GetType(String) 方法重载和描述类型的字符串,另一种方法调用构造类型 Dictionary<String, Example>(在 Visual Basic 中为 Dictionary(Of String, Example))的 GetGenericTypeDefinition 方法。 MakeGenericType 方法需要泛型类型定义。

      // Use the typeof operator to create the generic type 
      // definition directly. To specify the generic type definition,
      // omit the type arguments but retain the comma that separates
      // them.
      Type d1 = typeof(Dictionary<,>);
      // You can also obtain the generic type definition from a
      // constructed class. In this case, the constructed class
      // is a dictionary of Example objects, with String keys.
      Dictionary<string, Example> d2 = new Dictionary<string, Example>();
      // Get a Type object that represents the constructed type,
      // and from that get the generic type definition. The 
      // variables d1 and d4 contain the same type.
      Type d3 = d2.GetType();
      Type d4 = d3.GetGenericTypeDefinition();
             

      下面的代码示例定义 DisplayGenericType 方法来检查泛型类型定义和代码中使用的构造类型,并显示它们的信息。 DisplayGenericType 方法演示如何使用 IsGenericTypeIsGenericParameterGenericParameterPosition 属性以及 GetGenericArguments 方法。

      该示例还定义 DisplayGenericParameter 方法来检查泛型类型参数并显示其约束。

      代码示例定义一组测试类型,包括说明类型参数约束的泛型类型,并演示如何显示这些类型的信息。

      示例通过创建一组类型参数并调用 MakeGenericType 方法,从 Dictionary<TKey, TValue> 类构造类型。 程序对使用 MakeGenericType 构造的 Type 对象和使用 typeof(Visual Basic 中为 GetType)获取的 Type 对象进行比较,演示这两个对象是相同的。 类似地,程序使用 GetGenericTypeDefinition 方法获取构造类型的泛型类型定义,并将其与表示 Dictionary<TKey, TValue> 类的 Type 对象进行比较。

      using System.Reflection; using System.Collections.Generic; using System.Security.Permissions; // Define an example interface. public interface ITestArgument {} // Define an example base class. public class TestBase {} // Define a generic class with one parameter. The parameter // has three constraints: It must inherit TestBase, it must // implement ITestArgument, and it must have a parameterless // constructor. public class Test<T> where T : TestBase, ITestArgument, new() {} // Define a class that meets the constraints on the type // parameter of class Test. public class TestArgument : TestBase, ITestArgument public TestArgument() {} public class Example // The following method displays information about a generic // type. private static void DisplayGenericType(Type t) Console.WriteLine("\r\n {0}", t); Console.WriteLine(" Is this a generic type? {0}", t.IsGenericType); Console.WriteLine(" Is this a generic type definition? {0}", t.IsGenericTypeDefinition); // Get the generic type parameters or type arguments. Type[] typeParameters = t.GetGenericArguments(); Console.WriteLine(" List {0} type arguments:", typeParameters.Length); foreach( Type tParam in typeParameters ) if (tParam.IsGenericParameter) DisplayGenericParameter(tParam); Console.WriteLine(" Type argument: {0}", tParam); // The following method displays information about a generic // type parameter. Generic type parameters are represented by // instances of System.Type, just like ordinary types. private static void DisplayGenericParameter(Type tp) Console.WriteLine(" Type parameter: {0} position {1}", tp.Name, tp.GenericParameterPosition); Type classConstraint = null; foreach(Type iConstraint in tp.GetGenericParameterConstraints()) if (iConstraint.IsInterface) Console.WriteLine(" Interface constraint: {0}", iConstraint); if (classConstraint != null) Console.WriteLine(" Base type constraint: {0}", tp.BaseType); Console.WriteLine(" Base type constraint: None"); GenericParameterAttributes sConstraints = tp.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask; if (sConstraints == GenericParameterAttributes.None) Console.WriteLine(" No special constraints."); if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.DefaultConstructorConstraint)) Console.WriteLine(" Must have a parameterless constructor."); if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.ReferenceTypeConstraint)) Console.WriteLine(" Must be a reference type."); if (GenericParameterAttributes.None != (sConstraints & GenericParameterAttributes.NotNullableValueTypeConstraint)) Console.WriteLine(" Must be a non-nullable value type."); [PermissionSetAttribute(SecurityAction.Demand, Name="FullTrust")] public static void Main() // Two ways to get a Type object that represents the generic // type definition of the Dictionary class. // Use the typeof operator to create the generic type // definition directly. To specify the generic type definition, // omit the type arguments but retain the comma that separates // them. Type d1 = typeof(Dictionary<,>); // You can also obtain the generic type definition from a // constructed class. In this case, the constructed class // is a dictionary of Example objects, with String keys. Dictionary<string, Example> d2 = new Dictionary<string, Example>(); // Get a Type object that represents the constructed type, // and from that get the generic type definition. The // variables d1 and d4 contain the same type. Type d3 = d2.GetType(); Type d4 = d3.GetGenericTypeDefinition(); // Display information for the generic type definition, and // for the constructed type Dictionary<String, Example>. DisplayGenericType(d1); DisplayGenericType(d2.GetType()); // Construct an array of type arguments to substitute for // the type parameters of the generic Dictionary class. // The array must contain the correct number of types, in // the same order that they appear in the type parameter // list of Dictionary. The key (first type parameter) // is of type string, and the type to be contained in the // dictionary is Example. Type[] typeArgs = {typeof(string), typeof(Example)}; // Construct the type Dictionary<String, Example>. Type constructed = d1.MakeGenericType(typeArgs); DisplayGenericType(constructed); object o = Activator.CreateInstance(constructed); Console.WriteLine("\r\nCompare types obtained by different methods:"); Console.WriteLine(" Are the constructed types equal? {0}", (d2.GetType()==constructed)); Console.WriteLine(" Are the generic definitions equal? {0}", (d1==constructed.GetGenericTypeDefinition())); // Demonstrate the DisplayGenericType and // DisplayGenericParameter methods with the Test class // defined above. This shows base, interface, and special // constraints. DisplayGenericType(typeof(Test<>));

      成员签名

      说明

      MyInstance[] FindXxx(filter, filterCriteria)

      查找并返回经过筛选的类型列表,或者在当前类型没有实现任何匹配筛选器的类型的情况下返回空数组。

      示例:Type.FindInterfaces

      MyInstance GetXxx(<parameters>)

      返回由 <parameters> 唯一指定的类型。 如果不存在这样的类型,成员将返回空引用(在 Visual Basic 中为 Nothing)。 请注意,<parameters> 唯一地指定一个实例。

      示例:Type.GetInterface

      MyInstance[] GetXxxs()

      返回所有公共类型。 如果不存在公共类型,成员将返回空数组。

      示例:Type.GetFields

      MyInstance[] GetXxxs(<parameters>)

      返回由 <parameters> 指定的所有类型。 如果不存在这样的类型,成员将返回空数组。 请注意,<parameters> 并不一定指定唯一的实例。

      另一个常用的设计模式是使用委托 它们通常在反射中用来支持对返回对象数组的方法的结果集进行筛选。

      反射提供了获取有关类型和成员的信息和访问成员(即,调用方法和构造函数、获取和设置属性值、添加和移除事件处理程序等)的能力。 使用反射获取有关类型和成员的信息不受限制。 所有代码都可以使用反射来执行以下任务:

      • 枚举类型和成员并验证其元数据。

      • 枚举并验证程序集和模块。

      相反,使用反射访问成员将受到限制。 从 .NET Framework 4 版开始,仅受信任的代码可以使用反射访问安全关键成员。而且,仅受信任的代码才能使用反射访问编译代码不可直接访问的非公共成员。最后,使用反射访问安全关键成员的代码必须具有安全关键成员要求的任何权限,就像使用编译代码一样。

      根据必要的权限,代码可以使用反射来执行以下各种访问:

      • 访问不是安全关键的公共成员。

      • 访问可由已编译代码访问的非公共成员(如果这些成员不是安全关键)。 此类非公共成员的示例包括:

        • 调用代码的基类的受保护成员。 (在反射中,将此称为系列级访问。)

        • 调用代码的程序集中的 internal 成员(在 Visual Basic 中为 Friend 成员)。 (在反射中,将此称为程序集级访问。)

        • 包含调用代码的类的其他实例的私有成员。

      例如,在沙盒应用程序域中运行的代码只限于此列表中描述的访问,除非应用程序域授予其他权限。

      从 .NET Framework 2.0 版 Service Pack 1 开始,如果尝试访问通常不可访问的成员,则会生成针对目标对象的授予集以及带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission 的要求。 以完全信任方式运行的代码(例如,从命令行启动的应用程序中的代码)始终可以满足这些权限。 (这会受到有关访问安全关键的成员的限制,如本文后面所述。)

      或者,沙盒应用程序域可以授予带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission,如本文后面的访问通常不可访问的成员节中所述。

      访问安全关键成员

      如果一个成员满足以下条件之一,则为安全关键成员:具有 SecurityCriticalAttribute;属于一个具有 SecurityCriticalAttribute 的类型;或者位于一个安全关键程序集中。 从 .NET Framework 4 版开始,访问安全关键成员的规则如下所示:

      • 透明代码不能使用反射来访问安全关键成员,即使代码是完全受信任的也是如此。 将引发 MethodAccessExceptionFieldAccessExceptionTypeAccessException

      • 以部分信任方式运行的代码将被视为透明的。

      无论安全关键成员是由已编辑代码直接访问,还是通过使用反射访问,这些规则都是相同的。

      从命令行运行的应用程序代码以完全信任方式运行。 只要代码不被标记为透明的,它就可以使用反射来访问安全关键成员。 当相同的代码以部分信任方式运行(例如,在沙盒应用程序域中)时,程序集的信任级别将确定它是否可以访问安全关键代码:如果程序集具有强名称并在全局程序集缓存中进行安装,则它是一个受信任的程序集并可以调用安全关键成员。 如果它不是受信任的,则它将成为透明的(即使它未被标记为透明的),并且不能访问安全关键成员。

      有关 .NET Framework 4 中的安全模型的更多信息,请参见 .NET Framework 4 中的安全性更改

      从 .NET Framework 4 开始,公共语言运行时将根据多个因素来确定类型或成员的透明度级别,这些因素包括程序集的信任级别和应用程序域的信任级别。 反射提供 IsSecurityCriticalIsSecuritySafeCriticalIsSecurityTransparent 属性以使您能够发现类型的透明度级别。 下表显示了这些属性的有效组合。

      安全级别

      IsSecurityCritical

      IsSecuritySafeCritical

      IsSecurityTransparent

      Critical

      true

      false

      false

      安全关键的

      true

      true

      false

      透明

      false

      false

      true

      相对于检查程序集及其类型的安全批注、检查当前信任级别以及尝试复制运行时的规则,使用这些属性要简单很多。 例如,同一个类型既可以是安全关键的(当从命令行中运行时),也可以是安全透明的(当在沙盒应用程序域中运行时)。

      MethodBaseFieldInfoTypeBuilderMethodBuilderDynamicMethod 类有相似的属性。 (对于其他反射和反射发出抽象,将对关联的方法应用安全特性。例如,对于属性,安全特性将应用于属性访问器。)

      访问通常不可访问的成员

      若要使用反射来调用根据公共语言运行时的可访问性规则不可访问的成员,必须向您的代码授予以下两种权限之一:

      • 若要允许代码调用任何非公共成员,必须为您的代码授予带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission

        注意

        默认情况下,安全策略拒绝向源自 Internet 的代码授予此权限。 决不应向源自 Internet 的代码授予此权限。

      • 若要允许代码调用任何非公共成员(前提是包含被调用成员的程序集的授予集与包含调用代码的程序集的授予集或其子集相同),必须为您的代码授予带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission

      例如,假定向某个应用程序域授予 Internet 权限以及带有 ReflectionPermissionFlag.RestrictedMemberAccess 标记的 ReflectionPermission,然后运行具有 A 和 B 两个程序集的 Internet 应用程序。

      • 因为程序集 B 的授权集不包含任何未授予 A 的权限,因此程序集 A 可以使用反射访问程序集 B 的私有成员。

      • 程序集 A 不能使用反射来访问 .NET Framework 程序集(例如 mscorlib.dll)的私有成员,其原因是:mscorlib.dll 是完全受信任的,因此它具有尚未授予程序集 A 的权限。 当代码访问安全性在运行时审核堆栈时,将引发 MemberAccessException

      • 从 .NET Framework 4 版开始,透明代码将不能使用反射来访问安全关键成员。

      • ReflectionPermissionFlag.RestrictedMemberAccess 标志是在 .NET Framework 2.0 版 Service Pack 1 中引入的。 对于使用反射访问非公共成员的代码,早期版本的 .NET Framework 需要 ReflectionPermissionFlag.MemberAccess 标志。 决不应将此权限授予部分受信任的代码。

      • 从 .NET Framework 2.0 开始,使用反射获取有关非公共类型和成员的信息不需要任何权限。 在早期版本中,带有 ReflectionPermissionFlag.TypeInformation 标志的 ReflectionPermission 是必需的。

      反射提供语言编译器(如 Microsoft Visual Basic 2005 和 JScript)用于实现隐式后期绑定的基础结构。 绑定是查找与唯一指定的类型相对应的声明(即实现)的过程。 如果此过程是在运行时而不是在编译时发生,则称其为“后期绑定”。 利用 Visual Basic 2005,可以在代码中使用隐式后期绑定;Visual Basic 编译器会调用一个帮助器方法,该方法使用反射来获取对象类型。 传递给帮助器方法的参数有助于在运行时调用正确的方法。 这些参数包括:对其调用方法的实例(对象),被调用方法的名称(字符串),以及传递给被调用方法的参数(对象数组)。

      在下面的示例中,Visual Basic 编译器使用反射隐式地对其类型在编译时未知的对象调用方法。 HelloWorld 类具有一个 PrintHello 方法,它输出与传递给 PrintHello 方法的某些文本串联的“Hello World”。 在此示例中调用的 PrintHello 方法实际上是 Type.InvokeMember;Visual Basic 代码允许按照对象 (helloObj) 的类型在编译时已知(早期绑定)而不是在运行时已知(后期绑定)的方式来调用 PrintHello 方法。

      ' Creates the object. helloObj = new HelloWorld() ' Invokes the print method as if it was early bound ' even though it is really late bound. helloObj.PrintHello("Visual Basic Late Bound") End Sub End Module

      除了由编译器隐式地用来进行后期绑定之外,反射还可以在代码中显式地用来完成后期绑定。

      公共语言运行时支持多种编程语言,但这些语言的绑定规则各不相同。 在早期绑定的情况下,代码生成器可以完全控制此绑定。 但是,当通过反射进行后期绑定时,必须用自定义绑定来控制绑定。 Binder 类提供了对成员选择和调用的自定义控制。

      利用自定义绑定,您可以在运行时加载程序集,获取有关该程序集中类型的信息,然后对该类型调用方法或访问该类型的字段或属性。 如果您在编译时(例如当对象类型依赖于用户输入时)不知道对象的类型,就可以使用这种方法。

      下面的示例说明不提供参数类型转换的简单的自定义联编程序。 Simple_Type.dll 的代码位于示例主体之前。 确保生成 Simple_Type.dll,然后在生成时在项目中包括对它的引用。

      public void MyMethod(string str, int i) Console.WriteLine("MyMethod parameters: {0}, {1}", str, i); public void MyMethod(string str, int i, int j) Console.WriteLine("MyMethod parameters: {0}, {1}, {2}", str, i, j); namespace Custom_Binder class MyMainClass static void Main() // Get the type of MySimpleClass. Type myType = typeof(MySimpleClass); // Get an instance of MySimpleClass. MySimpleClass myInstance = new MySimpleClass(); MyCustomBinder myCustomBinder = new MyCustomBinder(); // Get the method information for the particular overload // being sought. MethodInfo myMethod = myType.GetMethod("MyMethod", BindingFlags.Public | BindingFlags.Instance, myCustomBinder, new Type[] {typeof(string), typeof(int)}, null); Console.WriteLine(myMethod.ToString()); // Invoke the overload. myType.InvokeMember("MyMethod", BindingFlags.InvokeMethod, myCustomBinder, myInstance, new Object[] {"Testing...", (int)32}); // **************************************************** // A simple custom binder that provides no // argument type conversion. // **************************************************** class MyCustomBinder : Binder public override MethodBase BindToMethod( BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state) if (match == null) throw new ArgumentNullException("match"); // Arguments are not being reordered. state = null; // Find a parameter match and return the first method with // parameters that match the request. foreach (MethodBase mb in match) ParameterInfo[] parameters = mb.GetParameters(); if (ParametersMatch(parameters, args)) return mb; return null; public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) if (match == null) throw new ArgumentNullException("match"); foreach (FieldInfo fi in match) if (fi.GetType() == value.GetType()) return fi; return null; public override MethodBase SelectMethod( BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) if (match == null) throw new ArgumentNullException("match"); // Find a parameter match and return the first method with // parameters that match the request. foreach (MethodBase mb in match) ParameterInfo[] parameters = mb.GetParameters(); if (ParametersMatch(parameters, types)) return mb; return null; public override PropertyInfo SelectProperty( BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) if (match == null) throw new ArgumentNullException("match"); foreach (PropertyInfo pi in match) if (pi.GetType() == returnType && ParametersMatch(pi.GetIndexParameters(), indexes)) return pi; return null; public override object ChangeType( object value, Type myChangeType, CultureInfo culture) object newType; newType = Convert.ChangeType(value, myChangeType); return newType; // Throw an InvalidCastException if the conversion cannot // be done by the Convert.ChangeType method. catch (InvalidCastException) return null; public override void ReorderArgumentArray(ref object[] args, object state) // No operation is needed here because BindToMethod does not // reorder the args array. The most common implementation // of this method is shown below. // ((BinderState)state).args.CopyTo(args, 0); // Returns true only if the type of each object in a matches // the type of each corresponding object in b. private bool ParametersMatch(ParameterInfo[] a, object[] b) if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) if (a[i].ParameterType != b[i].GetType()) return false; return true; // Returns true only if the type of each object in a matches // the type of each corresponding entry in b. private bool ParametersMatch(ParameterInfo[] a, Type[] b) if (a.Length != b.Length) return false; for (int i = 0; i < a.Length; i++) if (a[i].ParameterType != b[i]) return false; return true;

      InvokeMember 和 CreateInstance

      使用 Type.InvokeMember 可调用类型的成员。 各个类(如 System.ActivatorSystem.Reflection.Assembly)的 CreateInstance 方法是 InvokeMember 的特殊形式,它们可新建特定类型的实例。 Binder 类用于在这些方法中进行重载决策和参数强制。

      下面的示例显示参数强制(类型转换)和成员选择的三种可能的组合。 在第 1 种情况中,不需要任何参数强制或成员选择。 在第 2 种情况中,只需要成员选择。 在第 3 种情况中,只需要参数强制。

      Type t = typeof(CustomBinderDriver); CustomBinder binder = new CustomBinder(); BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static; object[] args; // Case 1. Neither argument coercion nor member selection is needed. args = new object[] {}; t.InvokeMember("PrintBob", flags, binder, null, args); // Case 2. Only member selection is needed. args = new object[] {42}; t.InvokeMember("PrintValue", flags, binder, null, args); // Case 3. Only argument coercion is needed. args = new object[] {"5.5"}; t.InvokeMember("PrintNumber", flags, binder, null, args); public static void PrintBob() Console.WriteLine("PrintBob"); public static void PrintValue(long value) Console.WriteLine("PrintValue({0})", value); public static void PrintValue(string value) Console.WriteLine("PrintValue\"{0}\")", value); public static void PrintNumber(double value) Console.WriteLine("PrintNumber ({0})", value);

      当多个成员具有相同的名称时,将需要重载决策。 Binder.BindToMethodBinder.BindToField 方法用于解析与单个成员的绑定。 Binder.BindToMethod 还通过 getset 属性访问器提供了属性解析。

      BindToMethod 返回要调用的 MethodBase;如果无法进行这种调用,则返回 null 引用(在 Visual Basic 中为 Nothing)。 虽然 MethodBase 返回值通常是 match 参数中所包含的值之一,但它并不必如此。

      当存在 ByRef 参数时,调用方可能需要取回这些参数。 因此,如果 BindToMethod 已经操作参数数组,Binder 会允许客户端将参数数组映射回它的初始形式。 为了实现这一目的,必须向调用方保证参数的顺序不会改变。 当按名称传递参数时,Binder 将重新排列参数数组,这就是调用方所见的参数。 有关更多信息,请参见 Binder.ReorderArgumentArray

      可用成员集包括在类型和任何基类型中定义的成员。 如果指定 BindingFlags.NonPublic,将返回该成员集中具有任何可访问性的成员。 如果未指定 BindingFlags.NonPublic,联编程序就必须强制可访问性规则。 当指定 PublicNonPublic 绑定标志时,还必须指定 InstanceStatic 绑定标志,否则不会返回任何成员。

      如果只有一个成员具有给定名称,则不必进行回调,而在该方法上进行绑定。 代码示例的第 1 种情况说明了这一点:只有一个 PrintBob 方法可用,因此不需要进行回调。

      如果可用集中有多个成员,所有这些方法都将传递给 BindToMethod,它将选择正确的方法并将其返回。 在代码示例的第 2 种情况下,有两个名为 PrintValue 的方法。 BindToMethod 的调用将选择正确的方法。

      ChangeType 执行参数强制转换(类型转换),以便将实参转换为选定方法的形参的类型。 即使类型完全匹配,也会为每个参数调用 ChangeType

      在代码示例的第 3 种情况下,将类型为 String 值为“5.5”的实参传递给了具有类型为 Double 的形参的方法。 要使调用成功,必须将字符串值“5.5”转换为 double 值。 ChangeType 会执行此转换。

      ChangeType 仅执行无损或扩大强制,如下表所示。

      源类型

      目标类型

      任何类型

      它的基类型

      任何类型

      它所实现的接口

      Char

      UInt16、UInt32、Int32、UInt64、Int64、Single、Double

      Byte

      Char、UInt16、Int16、UInt32、Int32、UInt64、Int64、Single、Double

      SByte

      Int16、Int32、Int64、Single、Double

      UInt16

      UInt32、Int32、UInt64、Int64、Single、Double

      Int16

      Int32、Int64、Single、Double

      UInt32

      UInt64、Int64、Single、Double

      Int32

      Int64、Single、Double

      UInt64

      Single、Double

      Int64

      Single、Double

      Single

      Double

      非引用类型

      引用类型

      Type 类具有 Get 方法,这些方法使用 Binder 类型的参数来解析对特定成员的引用。 Type.GetConstructorType.GetMethodType.GetProperty 通过提供当前类型的特定成员的签名信息来搜索该成员。 Binder.SelectMethodBinder.SelectProperty 进行回调以选择适当方法的给定签名信息。

      只反射加载上下文使您能够检查为其他平台或 .NET Framework 的其他版本编译的程序集。 加载到此上下文中的代码只能检查,不能执行。 这意味着无法创建对象,因为无法执行构造函数。 因为该代码无法执行,所以不会自动加载依赖项。 如果需要对依赖项进行检查,则必须自行加载。

      将程序集加载到只反射加载上下文中

      1. 使用 ReflectionOnlyLoad(String) 方法重载可加载给定了显示名称的程序集,而使用 ReflectionOnlyLoadFrom 方法可加载给定了路径的程序集。 如果该程序集为二进制文件映像,则使用 ReflectionOnlyLoad(Byte[]) 方法重载。

        注意

        您不能使用只反射上下文从非执行上下文中的 .NET Framework 版本加载 mscorlib.dll 版本。

      2. 如果该程序集具有依赖项,ReflectionOnlyLoad 方法不会加载这些依赖项。 如果需要对依赖项进行检查,则必须自行加载。

      3. 使用程序集的 ReflectionOnly 属性确定是否将该程序集加载到了只反射上下文中。

      4. 如果向该程序集或程序集中的类型应用了特性,则应通过使用 CustomAttributeData 类检查这些特性,以确保未尝试在只反射上下文中执行代码。 使用 CustomAttributeData.GetCustomAttributes 方法的适当重载可获取表示应用于程序集、成员、模块或参数的特性的 CustomAttributeData 对象。

        注意

        应用于该程序集或其内容的特性可能是在该程序集中定义的,也可能是在加载到只反射上下文中的另一个程序集中定义的。 无法事先知道这些特性是在何处定义的。

        下面的代码示例演示如何检查应用于加载到只反射上下文中的程序集的特性。

        该代码示例定义了一个带有两个构造函数和一个属性的自定义特性。 该特性可应用于程序集、在该程序集中声明的类型、该类型的方法以及该方法的参数。 在执行时,该程序集将其本身加载到只反射上下文中,并显示应用到它及它包含的类型和成员的自定义特性的有关信息。

        注意

        为简化该代码示例,该程序集自行完成加载和检查操作。 通常情况下,不要在执行上下文和只反射上下文中加载同一程序集。

        using System.Reflection; using System.Collections.Generic; using System.Collections.ObjectModel; // The example attribute is applied to the assembly. [assembly:Example(ExampleKind.ThirdKind, Note="This is a note on the assembly.")] // An enumeration used by the ExampleAttribute class. public enum ExampleKind FirstKind, SecondKind, ThirdKind, FourthKind // An example attribute. The attribute can be applied to all // targets, from assemblies to parameters. [AttributeUsage(AttributeTargets.All)] public class ExampleAttribute : Attribute // Data for properties. private ExampleKind kindValue; private string noteValue; private string[] arrayStrings; private int[] arrayNumbers; // Constructors. The parameterless constructor (.ctor) calls // the constructor that specifies ExampleKind and an array of // strings, and supplies the default values. public ExampleAttribute(ExampleKind initKind, string[] initStrings) kindValue = initKind; arrayStrings = initStrings; public ExampleAttribute(ExampleKind initKind) : this(initKind, null) {} public ExampleAttribute() : this(ExampleKind.FirstKind, null) {} // Properties. The Note and Numbers properties must be read/write, so they // can be used as named parameters. public ExampleKind Kind { get { return kindValue; }} public string[] Strings { get { return arrayStrings; }} public string Note get { return noteValue; } set { noteValue = value; } public int[] Numbers get { return arrayNumbers; } set { arrayNumbers = value; } // The example attribute is applied to the test class. [Example(ExampleKind.SecondKind, new string[] { "String array argument, line 1", "String array argument, line 2", "String array argument, line 3" }, Note="This is a note on the class.", Numbers = new int[] { 53, 57, 59 })] public class Test // The example attribute is applied to a method, using the // parameterless constructor and supplying a named argument. // The attribute is also applied to the method parameter. [Example(Note="This is a note on a method.")] public void TestMethod([Example] object arg) { } // Main() gets objects representing the assembly, the test // type, the test method, and the method parameter. Custom // attribute data is displayed for each of these. public static void Main() Assembly asm = Assembly.ReflectionOnlyLoad("Source"); Type t = asm.GetType("Test"); MethodInfo m = t.GetMethod("TestMethod"); ParameterInfo[] p = m.GetParameters(); Console.WriteLine("\r\nAttributes for assembly: '{0}'", asm); ShowAttributeData(CustomAttributeData.GetCustomAttributes(asm)); Console.WriteLine("\r\nAttributes for type: '{0}'", t); ShowAttributeData(CustomAttributeData.GetCustomAttributes(t)); Console.WriteLine("\r\nAttributes for member: '{0}'", m); ShowAttributeData(CustomAttributeData.GetCustomAttributes(m)); Console.WriteLine("\r\nAttributes for parameter: '{0}'", p); ShowAttributeData(CustomAttributeData.GetCustomAttributes(p[0])); private static void ShowAttributeData( IList<CustomAttributeData> attributes) foreach( CustomAttributeData cad in attributes ) Console.WriteLine(" {0}", cad); Console.WriteLine(" Constructor: '{0}'", cad.Constructor); Console.WriteLine(" Constructor arguments:"); foreach( CustomAttributeTypedArgument cata in cad.ConstructorArguments ) ShowValueOrArray(cata); Console.WriteLine(" Named arguments:"); foreach( CustomAttributeNamedArgument cana in cad.NamedArguments ) Console.WriteLine(" MemberInfo: '{0}'", cana.MemberInfo); ShowValueOrArray(cana.TypedValue); private static void ShowValueOrArray(CustomAttributeTypedArgument cata) if (cata.Value.GetType() == typeof(ReadOnlyCollection<CustomAttributeTypedArgument>)) Console.WriteLine(" Array of '{0}':", cata.ArgumentType); foreach (CustomAttributeTypedArgument cataElement in (ReadOnlyCollection<CustomAttributeTypedArgument>) cata.Value) Console.WriteLine(" Type: '{0}' Value: '{1}'", cataElement.ArgumentType, cataElement.Value); Console.WriteLine(" Type: '{0}' Value: '{1}'", cata.ArgumentType, cata.Value); /* This code example produces output similar to the following: Attributes for assembly: 'source, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' [System.Runtime.CompilerServices.CompilationRelaxationsAttribute((Int32)8)] Constructor: 'Void .ctor(Int32)' Constructor arguments: Type: 'System.Int32' Value: '8' Named arguments: [System.Runtime.CompilerServices.RuntimeCompatibilityAttribute(WrapNonExceptionThrows = True)] Constructor: 'Void .ctor()' Constructor arguments: Named arguments: MemberInfo: 'Boolean WrapNonExceptionThrows' Type: 'System.Boolean' Value: 'True' [ExampleAttribute((ExampleKind)2, Note = "This is a note on the assembly.")] Constructor: 'Void .ctor(ExampleKind)' Constructor arguments: Type: 'ExampleKind' Value: '2' Named arguments: MemberInfo: 'System.String Note' Type: 'System.String' Value: 'This is a note on the assembly.' Attributes for type: 'Test' [ExampleAttribute((ExampleKind)1, new String[3] { "String array argument, line 1", "String array argument, line 2", "String array argument, line 3" }, Note = "This is a note on the class.", Numbers = new Int32[3] { 53, 57, 59 })] Constructor: 'Void .ctor(ExampleKind, System.String[])' Constructor arguments: Type: 'ExampleKind' Value: '1' Array of 'System.String[]': Type: 'System.String' Value: 'String array argument, line 1' Type: 'System.String' Value: 'String array argument, line 2' Type: 'System.String' Value: 'String array argument, line 3' Named arguments: MemberInfo: 'System.String Note' Type: 'System.String' Value: 'This is a note on the class.' MemberInfo: 'Int32[] Numbers' Array of 'System.Int32[]': Type: 'System.Int32' Value: '53' Type: 'System.Int32' Value: '57' Type: 'System.Int32' Value: '59' Attributes for member: 'Void TestMethod(System.Object)' [ExampleAttribute(Note = "This is a note on a method.")] Constructor: 'Void .ctor()' Constructor arguments: Named arguments: MemberInfo: 'System.String Note' Type: 'System.String' Value: 'This is a note on a method.' Attributes for parameter: 'System.Object arg' [ExampleAttribute()] Constructor: 'Void .ctor()' Constructor arguments: Named arguments:
        Class1 c = new Class1();
        object o;
        o = c.GetType().InvokeMember("", BindingFlags.InvokeMethod, null, c, new object[0]);
        Console.WriteLine("Default member result: {0}", o);
                   
        Type classType = typeof(Class1);
        Type attribType = typeof(DefaultMemberAttribute);
        DefaultMemberAttribute defMem =
            (DefaultMemberAttribute)Attribute.GetCustomAttribute((MemberInfo)classType, attribType);
        MemberInfo[] memInfo = classType.GetMember(defMem.MemberName);
                   
        Type t = typeof(Class1);
        object[] customAttribs = t.GetCustomAttributes(typeof(DefaultMemberAttribute), false);
        if (customAttribs.Length > 0)
            DefaultMemberAttribute defMem = (DefaultMemberAttribute)customAttribs[0];
            MemberInfo[] memInfo = t.GetMember(defMem.MemberName);
            if (memInfo.Length > 0)
                Console.WriteLine("Default Member: {0}", memInfo[0].Name);
              

        若要使用反射检索实参的默认值,请获取形参的 ParameterInfo 对象,然后使用 ParameterInfo.DefaultValue 属性检索默认值。 如果不存在默认值,该属性将返回 Value.DBNull

        下面的示例向控制台显示 MyMethod 的默认值。

        MethodInfo m = t.GetMethod("MyMethod");
        ParameterInfo[] ps = m.GetParameters();
        for (int i = 0; i < ps.Length; i++) 
            Console.WriteLine("Default Value == {0}", ps[i].DefaultValue);
              

        若要调用包含具有默认值的实参的方法,请使用 Type.Missing 作为 InvokeMember 方法的形参值。 这样,后期绑定服务就能够为指定的参数值使用默认值。 如果为没有默认值的参数传递 Type.Missing,则会引发 ArgumentException 需要特别注意的是,并非所有编译器绑定机制都会考虑 Type.Missing 的这些规则。 有些联编程序可能不支持此功能,或者可能以不同的方式处理 Type.Missing 当使用 Type.Missing 时,默认值不必是结尾的参数值。

        C# 语言不支持默认参数。

        下面的 Visual Basic 2005 示例演示如何使用反射调用具有默认参数的方法。

        Imports System.Reflection Public Class OptionalArg Public Sub MyMethod (a As Integer, Optional b As Double = 1.2, Optional c As Integer=1) Console.WriteLine("a = " & a & " b = " & b & " c = " & c) End Sub End Class Module Module1 Sub Main() Dim o As New OptionalArg Dim t As Type t = GetType(OptionalArg) Dim Param As Object()= {10, 20, 30} t.InvokeMember("MyMethod", _ BindingFlags.Public Or _ BindingFlags.Instance Or _ BindingFlags.InvokeMethod Or _ BindingFlags.OptionalParamBinding, _ Nothing, _ New Object() {10, 55.3, 12}) t.InvokeMember("MyMethod", _ BindingFlags.Public Or _ BindingFlags.Instance Or _ BindingFlags.InvokeMethod Or _ BindingFlags.OptionalParamBinding, _ Nothing, _ New Object() {10, 1.3, Type.Missing}) t.InvokeMember("MyMethod", _ BindingFlags.Public Or _ BindingFlags.Instance Or _ BindingFlags.InvokeMethod Or _ BindingFlags.OptionalParamBinding, _ Nothing, _ New Object() {10, Type.Missing, Type.Missing}) End Sub End Module

        当特性与程序元素相关联后,可以使用反射来查询它们是否存在以及它们的值。 在 .NET Framework 1.0 和 1.1 版中,是在执行上下文中检查自定义特性。 .NET Framework 2.0 版提供了新的加载上下文(只反射上下文),该上下文可用于检查无法加载执行的代码。

        只反射上下文

        加载到只反射上下文中的代码无法执行。 这意味着不能创建自定义特性的实例,因为这将需要执行其构造函数。 若要在只反射上下文中加载和检查自定义特性,请使用 CustomAttributeData 类。 可以通过使用静态 CustomAttributeData.GetCustomAttributes 方法的相应重载获取此类的实例。 请参见如何:将程序集加载到仅反射上下文中

        用于查询执行上下文中的特性的主要反射方法是 MemberInfo.GetCustomAttributesAttribute.GetCustomAttributes

        自定义特性的可访问性根据附加该特性的程序集来进行检查。 这相当于检查附加自定义特性的程序集中,其类型的方法是否可以调用自定义特性的构造函数。

        诸如 Assembly.GetCustomAttributes(Boolean) 等方法检查类型参数的可见性和可访问性。 只有包含用户定义类型的程序集中的代码才能使用 GetCustomAttributes 检索该类型的自定义特性。

        下面的 C# 示例是典型的自定义特性设计模式。 它说明运行时自定义特性反射模型。

        如果运行时尝试为附加到 GetLanguage 方法的公共自定义特性类型 DescriptionAttribute 检索自定义特性,则该运行时将执行下列操作:

        1. 运行时检查 Type.GetCustomAttributes(Type type) 的 DescriptionAttribute 类型参数是否为公共的,从而是否可见和访问。

        2. 运行时检查从 DescriptionAttribute 派生的用户定义类型 MyDescriptionAttribute System.Web.DLL 程序集(它在该程序集中附加到 GetLanguage() 方法)内是否可见和可以访问。

        3. 运行时检查 MyDescriptionAttribute 的构造函数是否在 System.Web.DLL 程序集中可见和可以访问。

        4. 运行时调用带有自定义特性参数的 MyDescriptionAttribute 的构造函数,然后将新对象返回给调用方。

        自定义特性反射模型可能会在定义类型的程序集外泄漏用户定义类型的实例。 这与运行时系统库中返回用户定义类型的实例的成员(例如返回 RuntimeMethodInfo 对象数组的 Type.GetMethods())相同。 为了防止客户端发现关于用户定义的自定义特性类型的信息,请将该类型的成员定义为非公共成员。

        下面的示例说明使用反射访问自定义特性的基本方法。

        System.Reflection.MemberInfo info = typeof(Class1); foreach (object attrib in info.GetCustomAttributes(true)) Console.WriteLine(attrib);

        要为各种反射操作提供有效的输入,必须指定类型名称。 完全限定的类型名称包含程序集名称说明、命名空间说明和类型名称。 类型名称说明由 Type.GetTypeModule.GetTypeModuleBuilder.GetTypeAssembly.GetType 等方法使用。

        类型名称的 Backus-Naur 形式语法

        Backus-Naur 形式 (BNF) 定义正式语言的语法。 下表中列出的 BNF 词法规则说明如何识别有效的输入。 最终元素(无法再减小的元素)全部以大写字母显示。 非最终元素(可以再减小的元素)则显示为大小写混合或带单引号的字符串,但单引号 (') 不是语法本身的一部分。 竖线 (|) 表示具有子规则的规则。

        完全限定类型名称的 BNF 语法

        TypeSpec                          :=   ReferenceTypeSpec

                                                |     SimpleTypeSpec

        ReferenceTypeSpec            :=   SimpleTypeSpec '&'

        SimpleTypeSpec                :=   PointerTypeSpec

                                                |     ArrayTypeSpec

                                                |     TypeName

        PointerTypeSpec                :=   SimpleTypeSpec '*'

        ArrayTypeSpec                  :=   SimpleTypeSpec '[ReflectionDimension]'

                                                |     SimpleTypeSpec '[ReflectionEmitDimension]'

        ReflectionDimension           :=   '*'

                                                |     ReflectionDimension ',' ReflectionDimension

                                                |     NOTOKEN

        ReflectionEmitDimension    :=   '*'

                                                |     Number '..'Number

                                                |     Number '…'

                                                |     ReflectionDimension ',' ReflectionDimension

                                                |     NOTOKEN

        Number                            :=   [0-9]+

        TypeName                         :=   NamespaceTypeName

                                                |     NamespaceTypeName ',' AssemblyNameSpec

        NamespaceTypeName        :=   NestedTypeName

                                                |     NamespaceSpec '.'NestedTypeName

        NestedTypeName               :=   IDENTIFIER

                                                |     NestedTypeName '+' IDENTIFIER

        NamespaceSpec                 :=   IDENTIFIER

                                                |     NamespaceSpec '.'IDENTIFIER

        AssemblyNameSpec           :=   IDENTIFIER

                                                |     IDENTIFIER ',' AssemblyProperties

        AssemblyProperties            :=   AssemblyProperty

                                                |     AssemblyProperties ',' AssemblyProperty

        AssemblyProperty              :=   AssemblyPropertyName '=' AssemblyPropertyValue

        标记

        含义

        \,

        程序集分隔符。

        \+

        嵌套类型分隔符。

        \&

        引用类型。

        \*

        指针类型。

        \[

        数组维度分隔符。

        \]

        数组维度分隔符。

        \.

        只有在数组说明中使用句点时,才应在句点前使用反斜杠。 NamespaceSpec 中的句点不采用反斜杠。

        \\

        用作字符串的反斜杠。

        请注意,在除 AssemblyNameSpec 之外的所有 TypeSpec 组成部分中,空格都是相关的。 在 AssemblyNameSpec 中,“,”分隔符之前的空格相关,但“,”分隔符之后的空格将被忽略。

        反射类(如 Type.FullName)返回改变后的名称,以便使返回的名称可以在对 GetType 的调用中(如同在 MyType.GetType(myType.FullName) 中一样)使用。

        例如,某个类型的完全限定名称可能是 Ozzy.OutBack.Kangaroo+Wallaby,MyAssembly

        如果命名空间为 Ozzy.Out+Back,则必须在加号前加反斜杠。 否则,分析器会将其解释为嵌套分隔符。 反射会将该字符串当作 Ozzy.Out\+Back.Kangaroo+Wallaby,MyAssembly 发出。

        指定程序集名称

        程序集名称说明所需的最少信息是程序集的文本名称 (IDENTIFIER)。 您可以在 IDENTIFIER 后添加下表所述的逗号分隔属性/值对列表。 IDENTIFIER 命名应遵循文件命名的规则。 IDENTIFIER 不区分大小写。

        属性名

        说明

        允许值

        版本

        程序集版本号

        Major.Minor.Build.Revision,其中 MajorMinorBuildRevision 是 0 和 65535 之间(含 0 和 65535)的整数。

        PublicKey

        完全公钥

        完全公钥的十六进制字符串值。 指定 null 引用(在 Visual Basic 中为 Nothing)以显式指示私有程序集。

        PublicKeyToken

        公钥标记(完全公钥的 8 字节哈希)

        公钥标记的十六进制字符串值。 指定 null 引用(在 Visual Basic 中为 Nothing)以显式指示私有程序集。

        Culture

        程序集区域性

        程序集的 RFC-1766 格式区域性,或者对于独立于语言(非附属)的程序集为“非特定”。

        自定义

        自定义的二进制大对象 (BLOB)。 它当前仅用于由本机图像生成器 (Ngen) 生成的程序集。

        自定义的字符串,由本机图像生成器工具用来向程序集缓存通知所安装的程序集为本机图像,因此将安装在本机图像缓存中。 也称作 Zap 字符串。

        下面的示例演示具有默认区域性的简单命名程序集的 AssemblyName

        com.microsoft.crypto, Culture="", PublicKeyToken=a5d015c7d5a0b012
        com.microsoft.crypto, Culture=en, PublicKeyToken=a5d015c7d5a0b012,
            Version=1.0.0.0
               

        在 BNF 语法中,ReflectionEmitDimension 仅适用于使用 ModuleBuilder.GetType 检索的不完整类型定义。 不完整的类型定义是使用 Reflection.Emit 构造但没有对它们调用 TypeBuilder.CreateTypeTypeBuilder 对象。 ReflectionDimension 可用于检索任何已完成的类型定义,即已加载的类型。

        通过指定数组的秩,可以访问反射中的数组:

        • Type.GetType("MyArray[]") 获取下限为 0 的单维数组。

        • Type.GetType("MyArray[*]") 获取下限未知的单维数组。

        • Type.GetType("MyArray[][]") 获取二维数组的数组。

        • Type.GetType("MyArray[*,*]")Type.GetType("MyArray[,]") 获取下限未知的矩形二维数组。

        请注意,从运行时的角度来看,MyArray[] != MyArray[*],但对于多维数组而言,这两种表示是等效的。 也就是说,Type.GetType("MyArray [,]") == Type.GetType("MyArray[*,*]") 的计算结果为 true

        对于 ModuleBuilder.GetTypeMyArray[0..5] 指示大小为 6 下限为 0 的单维数组。 MyArray[4…] 指示大小未知下限为 4 的单维数组。

        多数元数据都可使用托管反射来访问。 少数情况下,使用托管反射中提供的类很难检索元数据。 也有时托管反射并不能提供最佳性能。

        有关非托管反射 API 的信息,请参见 元数据接口 元数据结构元数据枚举中记录了该 API 使用的结构和枚举。

        注意

        使用非托管反射 API 需要熟悉 Common Language Infrastructure (CLI) 文档,尤其是“Partition II: Metadata Definition and Semantics”(第二部分:元数据定义和语义)和“Partition III: CIL Instruction Set”(第三部分:CIL 指令集)。 该文档可联机获得;请参见 MSDN 上的 ECMA C# and Common Language Infrastructure Standards(ECMA C# 和公共语言基础结构标准)和 Ecma International 网站上的 Standard ECMA-335 - Common Language Infrastructure (CLI)(标准 ECMA-335 - 公共语言基础结构 (CLI))。

        当使用反射来加载和运行程序集时,不能使用 C# += 运算符或 Visual Basic AddHandler 语句等语言功能将事件挂钩。 以下过程显示如何通过用反射获取所需的全部类型将现有方法挂钩到事件,以及如何使用反射发出来创建动态方法并将其挂钩到事件。

        注意

        有关事件处理委托的其他挂钩方式,请参见 EventInfo 类的 AddEventHandler 方法的代码示例。

        使用反射挂钩委托

        1. 加载包含引发事件的类型的程序集。 程序集通常使用 Assembly.Load 方法加载。 为了使此示例尽量简单,在当前程序集中使用了派生窗体,所以使用 GetExecutingAssembly 方法来加载当前程序集。

          Type returnType = GetDelegateReturnType(tDelegate);
          if (returnType != typeof(void))
              throw new ApplicationException("Delegate has a return type.");
          DynamicMethod handler = 
              new DynamicMethod("", 
                                null,
                                GetDelegateParameterTypes(tDelegate),
                                typeof(Example));
                       
          ILGenerator ilgen = handler.GetILGenerator();
          Type[] showParameters = { typeof(String) };
          MethodInfo simpleShow = 
              typeof(MessageBox).GetMethod("Show", showParameters);
          ilgen.Emit(OpCodes.Ldstr, 
              "This event handler was constructed at run time.");
          ilgen.Emit(OpCodes.Call, simpleShow);
          ilgen.Emit(OpCodes.Pop);
          ilgen.Emit(OpCodes.Ret);
                  // Load an assembly, for example using the Assembly.Load
                  // method. In this case, the executing assembly is loaded, to
                  // keep the demonstration simple.
                  Assembly assem = Assembly.GetExecutingAssembly();
                  // Get the type that is to be loaded, and create an instance 
                  // of it. Activator.CreateInstance has other overloads, if
                  // the type lacks a default constructor. The new instance
                  // is stored as type Object, to maintain the fiction that 
                  // nothing is known about the assembly. (Note that you can
                  // get the types in an assembly without knowing their names
                  // in advance.)
                  Type tExForm = assem.GetType("ExampleForm");
                  Object exFormAsObj = Activator.CreateInstance(tExForm);
                  // Get an EventInfo representing the Click event, and get the
                  // type of delegate that handles the event.
                  EventInfo evClick = tExForm.GetEvent("Click");
                  Type tDelegate = evClick.EventHandlerType;
                  // If you already have a method with the correct signature,
                  // you can simply get a MethodInfo for it. 
                  MethodInfo miHandler = 
                      typeof(Example).GetMethod("LuckyHandler", 
                          BindingFlags.NonPublic | BindingFlags.Instance);
                  // Create an instance of the delegate. Using the overloads
                  // of CreateDelegate that take MethodInfo is recommended.
                  Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
                  // Get the "add" accessor of the event and invoke it late-
                  // bound, passing in the delegate instance. This is equivalent
                  // to using the += operator in C#, or AddHandler in Visual
                  // Basic. The instance on which the "add" accessor is invoked
                  // is the form; the arguments must be passed as an array.
                  MethodInfo addHandler = evClick.GetAddMethod();
                  Object[] addHandlerArgs = { d };
                  addHandler.Invoke(exFormAsObj, addHandlerArgs);
                  // Event handler methods can also be generated at run time,
                  // using lightweight dynamic methods and Reflection.Emit. 
                  // To construct an event handler, you need the return type
                  // and parameter types of the delegate. These can be obtained
                  // by examining the delegate's Invoke method. 
                  // It is not necessary to name dynamic methods, so the empty 
                  // string can be used. The last argument associates the 
                  // dynamic method with the current type, giving the delegate
                  // access to all the public and private members of Example,
                  // as if it were an instance method.
                  Type returnType = GetDelegateReturnType(tDelegate);
                  if (returnType != typeof(void))
                      throw new ApplicationException("Delegate has a return type.");
                  DynamicMethod handler = 
                      new DynamicMethod("", 
                                        null,
                                        GetDelegateParameterTypes(tDelegate),
                                        typeof(Example));
                  // Generate a method body. This method loads a string, calls 
                  // the Show method overload that takes a string, pops the 
                  // return value off the stack (because the handler has no
                  // return type), and returns.
                  ILGenerator ilgen = handler.GetILGenerator();
                  Type[] showParameters = { typeof(String) };
                  MethodInfo simpleShow = 
                      typeof(MessageBox).GetMethod("Show", showParameters);
                  ilgen.Emit(OpCodes.Ldstr, 
                      "This event handler was constructed at run time.");
                  ilgen.Emit(OpCodes.Call, simpleShow);
                  ilgen.Emit(OpCodes.Pop);
                  ilgen.Emit(OpCodes.Ret);
                  // Complete the dynamic method by calling its CreateDelegate
                  // method. Use the "add" accessor to add the delegate to
                  // the invocation list for the event.
                  Delegate dEmitted = handler.CreateDelegate(tDelegate);
                  addHandler.Invoke(exFormAsObj, new Object[] { dEmitted });
                  // Show the form. Clicking on the form causes the two
                  // delegates to be invoked.
                  Application.Run((Form) exFormAsObj);
              private void LuckyHandler(Object sender, EventArgs e)
                  MessageBox.Show("This event handler just happened to be lying around.");
              private Type[] GetDelegateParameterTypes(Type d)
                  if (d.BaseType != typeof(MulticastDelegate))
                      throw new ApplicationException("Not a delegate.");
                  MethodInfo invoke = d.GetMethod("Invoke");
                  if (invoke == null)
                      throw new ApplicationException("Not a delegate.");
                  ParameterInfo[] parameters = invoke.GetParameters();
                  Type[] typeParameters = new Type[parameters.Length];
                  for (int i = 0; i < parameters.Length; i++)
                      typeParameters[i] = parameters[i].ParameterType;
                  return typeParameters;
              private Type GetDelegateReturnType(Type d)
                  if (d.BaseType != typeof(MulticastDelegate))
                      throw new ApplicationException("Not a delegate.");
                  MethodInfo invoke = d.GetMethod("Invoke");
                  if (invoke == null)
                      throw new ApplicationException("Not a delegate.");
                  return invoke.ReturnType;
                              反射(C# - vs2010)反射提供了描述程序集、模块和类型的对象(Type 类型)。 可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。 如果代码中使用了特性,可以利用反射来访问它们。 有关更多信息,请参见 利用特性扩展元数据。下面是使用静态方法 GetType(从 Object 基类派生的所有类型都继承该...
          				
          反射是.NET的一个功能,它允许你在运行时动态获取程序集、类型、成员等的信息,并且可以执行操作,比如创建对象、调用方法、获取属性等。 下面是一个使用反射在.NET创建对象的示例代码: using System; using System.Reflection; namespace ReflectionDemo class Program static void Main(string[] args) // 获取程序集 Assembly assembly = Assembly.Load("MyAssembly"); // 获取类型 Type type = assembly.GetType("MyType"); // 创建对象 object obj = Activator.CreateInstance(type); // 调用方法 MethodInfo method = type.GetMethod("MyMethod"); method.Invoke(obj, null); // 获取属性 PropertyInfo property = type.GetProperty("MyProperty"); object value = property.GetValue(obj); Console.WriteLine(value); 上面的代码,我们首先使用`Assembly.Load`方法获取程序集,然后使用`GetType`方法获取类型,接着使用`Activator.CreateInstance`方法创建对象,最后使用`GetMethod`和`Invoke`方法调用方法,使用`GetProperty`和`GetValue`方法获取属性的值。