GenericTypeParameterBuilder.SetGenericParameterAttributes 方法

定义

设置泛型参数的方差特征和特殊约束,例如无参数构造函数约束。

public:
 void SetGenericParameterAttributes(System::Reflection::GenericParameterAttributes genericParameterAttributes);
public void SetGenericParameterAttributes(System.Reflection.GenericParameterAttributes genericParameterAttributes);
member this.SetGenericParameterAttributes : System.Reflection.GenericParameterAttributes -> unit
Public Sub SetGenericParameterAttributes (genericParameterAttributes As GenericParameterAttributes)

参数

genericParameterAttributes
GenericParameterAttributes

GenericParameterAttributes 值的按位组合,用于表示泛型类型参数的方差特征和特殊约束。

示例

下面的代码示例创建一个具有两个类型参数的泛型类型,第一个类型被约束为具有无参数构造函数和引用类型,并将其保存在程序集 GenericEmitExample1.dll 中。 可以使用 Ildasm.exe (IL 反汇编程序) 查看生成的类型。 有关定义动态泛型类型所涉及的步骤的更详细说明,请参阅 如何:使用反射发出定义泛型类型

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

// Define a trivial base class and two trivial interfaces
// to use when demonstrating constraints.
//
public class ExampleBase {}

public interface IExampleA {}

public interface IExampleB {}

// Define a trivial type that can substitute for type parameter
// TSecond.
//
public class ExampleDerived : ExampleBase, IExampleA, IExampleB {}

public class Example
{
    public static void Main()
    {
        // Define a dynamic assembly to contain the sample type. The
        // assembly will not be run, but only saved to disk, so
        // AssemblyBuilderAccess.Save is specified.
        //
        AppDomain myDomain = AppDomain.CurrentDomain;
        AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
        AssemblyBuilder myAssembly =
            myDomain.DefineDynamicAssembly(myAsmName,
                AssemblyBuilderAccess.RunAndSave);

        // An assembly is made up of executable modules. For a single-
        // module assembly, the module name and file name are the same
        // as the assembly name.
        //
        ModuleBuilder myModule =
            myAssembly.DefineDynamicModule(myAsmName.Name,
               myAsmName.Name + ".dll");

        // Get type objects for the base class trivial interfaces to
        // be used as constraints.
        //
        Type baseType = typeof(ExampleBase);
        Type interfaceA = typeof(IExampleA);
        Type interfaceB = typeof(IExampleB);

        // Define the sample type.
        //
        TypeBuilder myType =
            myModule.DefineType("Sample", TypeAttributes.Public);

        Console.WriteLine("Type 'Sample' is generic: {0}",
            myType.IsGenericType);

        // Define type parameters for the type. Until you do this,
        // the type is not generic, as the preceding and following
        // WriteLine statements show. The type parameter names are
        // specified as an array of strings. To make the code
        // easier to read, each GenericTypeParameterBuilder is placed
        // in a variable with the same name as the type parameter.
        //
        string[] typeParamNames = {"TFirst", "TSecond"};
        GenericTypeParameterBuilder[] typeParams =
            myType.DefineGenericParameters(typeParamNames);

        GenericTypeParameterBuilder TFirst = typeParams[0];
        GenericTypeParameterBuilder TSecond = typeParams[1];

        Console.WriteLine("Type 'Sample' is generic: {0}",
            myType.IsGenericType);

        // Apply constraints to the type parameters.
        //
        // A type that is substituted for the first parameter, TFirst,
        // must be a reference type and must have a parameterless
        // constructor.
        TFirst.SetGenericParameterAttributes(
            GenericParameterAttributes.DefaultConstructorConstraint |
            GenericParameterAttributes.ReferenceTypeConstraint);

        // A type that is substituted for the second type
        // parameter must implement IExampleA and IExampleB, and
        // inherit from the trivial test class ExampleBase. The
        // interface constraints are specified as an array
        // containing the interface types.
        TSecond.SetBaseTypeConstraint(baseType);
        Type[] interfaceTypes = {interfaceA, interfaceB};
        TSecond.SetInterfaceConstraints(interfaceTypes);

        // The following code adds a private field named ExampleField,
        // of type TFirst.
        FieldBuilder exField =
            myType.DefineField("ExampleField", TFirst,
                FieldAttributes.Private);

        // Define a static method that takes an array of TFirst and
        // returns a List<TFirst> containing all the elements of
        // the array. To define this method it is necessary to create
        // the type List<TFirst> by calling MakeGenericType on the
        // generic type definition, List<T>. (The T is omitted with
        // the typeof operator when you get the generic type
        // definition.) The parameter type is created by using the
        // MakeArrayType method.
        //
        Type listOf = typeof(List<>);
        Type listOfTFirst = listOf.MakeGenericType(TFirst);
        Type[] mParamTypes = {TFirst.MakeArrayType()};

        MethodBuilder exMethod =
            myType.DefineMethod("ExampleMethod",
                MethodAttributes.Public | MethodAttributes.Static,
                listOfTFirst,
                mParamTypes);

        // Emit the method body.
        // The method body consists of just three opcodes, to load
        // the input array onto the execution stack, to call the
        // List<TFirst> constructor that takes IEnumerable<TFirst>,
        // which does all the work of putting the input elements into
        // the list, and to return, leaving the list on the stack. The
        // hard work is getting the constructor.
        //
        // The GetConstructor method is not supported on a
        // GenericTypeParameterBuilder, so it is not possible to get
        // the constructor of List<TFirst> directly. There are two
        // steps, first getting the constructor of List<T> and then
        // calling a method that converts it to the corresponding
        // constructor of List<TFirst>.
        //
        // The constructor needed here is the one that takes an
        // IEnumerable<T>. Note, however, that this is not the
        // generic type definition of IEnumerable<T>; instead, the
        // T from List<T> must be substituted for the T of
        // IEnumerable<T>. (This seems confusing only because both
        // types have type parameters named T. That is why this example
        // uses the somewhat silly names TFirst and TSecond.) To get
        // the type of the constructor argument, take the generic
        // type definition IEnumerable<T> (expressed as
        // IEnumerable<> when you use the typeof operator) and
        // call MakeGenericType with the first generic type parameter
        // of List<T>. The constructor argument list must be passed
        // as an array, with just one argument in this case.
        //
        // Now it is possible to get the constructor of List<T>,
        // using GetConstructor on the generic type definition. To get
        // the constructor of List<TFirst>, pass List<TFirst> and
        // the constructor from List<T> to the static
        // TypeBuilder.GetConstructor method.
        //
        ILGenerator ilgen = exMethod.GetILGenerator();

        Type ienumOf = typeof(IEnumerable<>);
        Type TfromListOf = listOf.GetGenericArguments()[0];
        Type ienumOfT = ienumOf.MakeGenericType(TfromListOf);
        Type[] ctorArgs = {ienumOfT};

        ConstructorInfo ctorPrep = listOf.GetConstructor(ctorArgs);
        ConstructorInfo ctor =
            TypeBuilder.GetConstructor(listOfTFirst, ctorPrep);

        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Newobj, ctor);
        ilgen.Emit(OpCodes.Ret);

        // Create the type and save the assembly.
        Type finished = myType.CreateType();
        myAssembly.Save(myAsmName.Name+".dll");

        // Invoke the method.
        // ExampleMethod is not generic, but the type it belongs to is
        // generic, so in order to get a MethodInfo that can be invoked
        // it is necessary to create a constructed type. The Example
        // class satisfies the constraints on TFirst, because it is a
        // reference type and has a default constructor. In order to
        // have a class that satisfies the constraints on TSecond,
        // this code example defines the ExampleDerived type. These
        // two types are passed to MakeGenericMethod to create the
        // constructed type.
        //
        Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
        Type constructed = finished.MakeGenericType(typeArgs);
        MethodInfo mi = constructed.GetMethod("ExampleMethod");

        // Create an array of Example objects, as input to the generic
        // method. This array must be passed as the only element of an
        // array of arguments. The first argument of Invoke is
        // null, because ExampleMethod is static. Display the count
        // on the resulting List<Example>.
        //
        Example[] input = {new Example(), new Example()};
        object[] arguments = {input};

        List<Example> listX =
            (List<Example>) mi.Invoke(null, arguments);

        Console.WriteLine(
            "\nThere are {0} elements in the List<Example>.",
            listX.Count);

        DisplayGenericParameters(finished);
    }

    private static void DisplayGenericParameters(Type t)
    {
        if (!t.IsGenericType)
        {
            Console.WriteLine("Type '{0}' is not generic.");
            return;
        }
        if (!t.IsGenericTypeDefinition)
        {
            t = t.GetGenericTypeDefinition();
        }

        Type[] typeParameters = t.GetGenericArguments();
        Console.WriteLine("\nListing {0} type parameters for type '{1}'.",
            typeParameters.Length, t);

        foreach( Type tParam in typeParameters )
        {
            Console.WriteLine("\r\nType parameter {0}:", tParam.ToString());

            foreach( Type c in tParam.GetGenericParameterConstraints() )
            {
                if (c.IsInterface)
                {
                    Console.WriteLine("    Interface constraint: {0}", c);
                }
                else
                {
                    Console.WriteLine("    Base type constraint: {0}", c);
                }
            }

            ListConstraintAttributes(tParam);
        }
    }

    // List the constraint flags. The GenericParameterAttributes
    // enumeration contains two sets of attributes, variance and
    // constraints. For this example, only constraints are used.
    //
    private static void ListConstraintAttributes(Type t)
    {
        // Mask off the constraint flags.
        GenericParameterAttributes constraints =
            t.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;

        if ((constraints & GenericParameterAttributes.ReferenceTypeConstraint)
            != GenericParameterAttributes.None)
        {
            Console.WriteLine("    ReferenceTypeConstraint");
        }

        if ((constraints & GenericParameterAttributes.NotNullableValueTypeConstraint)
            != GenericParameterAttributes.None)
        {
            Console.WriteLine("    NotNullableValueTypeConstraint");
        }

        if ((constraints & GenericParameterAttributes.DefaultConstructorConstraint)
            !=GenericParameterAttributes.None)
        {
            Console.WriteLine("    DefaultConstructorConstraint");
        }
    }
}

/* This code example produces the following output:

Type 'Sample' is generic: False
Type 'Sample' is generic: True

There are 2 elements in the List<Example>.

Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.

Type parameter TFirst:
    ReferenceTypeConstraint
    DefaultConstructorConstraint

Type parameter TSecond:
    Interface constraint: IExampleA
    Interface constraint: IExampleB
    Base type constraint: ExampleBase
 */
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Collections.Generic

' Define a trivial base class and two trivial interfaces 
' to use when demonstrating constraints.
'
Public Class ExampleBase
End Class

Public Interface IExampleA
End Interface

Public Interface IExampleB
End Interface

' Define a trivial type that can substitute for type parameter 
' TSecond.
'
Public Class ExampleDerived
    Inherits ExampleBase
    Implements IExampleA, IExampleB
End Class

Public Class Example
    Public Shared Sub Main()
        ' Define a dynamic assembly to contain the sample type. The
        ' assembly will not be run, but only saved to disk, so
        ' AssemblyBuilderAccess.Save is specified.
        '
        Dim myDomain As AppDomain = AppDomain.CurrentDomain
        Dim myAsmName As New AssemblyName("GenericEmitExample1")
        Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
            myAsmName, _
            AssemblyBuilderAccess.RunAndSave)

        ' An assembly is made up of executable modules. For a single-
        ' module assembly, the module name and file name are the same 
        ' as the assembly name. 
        '
        Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
            myAsmName.Name, _
            myAsmName.Name & ".dll")

        ' Get type objects for the base class trivial interfaces to
        ' be used as constraints.
        '
        Dim baseType As Type = GetType(ExampleBase)
        Dim interfaceA As Type = GetType(IExampleA)
        Dim interfaceB As Type = GetType(IExampleB)
                
        ' Define the sample type.
        '
        Dim myType As TypeBuilder = myModule.DefineType( _
            "Sample", _
            TypeAttributes.Public)

        Console.WriteLine("Type 'Sample' is generic: {0}", _
            myType.IsGenericType)

        ' Define type parameters for the type. Until you do this, 
        ' the type is not generic, as the preceding and following 
        ' WriteLine statements show. The type parameter names are
        ' specified as an array of strings. To make the code
        ' easier to read, each GenericTypeParameterBuilder is placed
        ' in a variable with the same name as the type parameter.
        ' 
        Dim typeParamNames() As String = {"TFirst", "TSecond"}
        Dim typeParams() As GenericTypeParameterBuilder = _
            myType.DefineGenericParameters(typeParamNames)

        Dim TFirst As GenericTypeParameterBuilder = typeParams(0)
        Dim TSecond As GenericTypeParameterBuilder = typeParams(1)

        Console.WriteLine("Type 'Sample' is generic: {0}", _
            myType.IsGenericType)

        ' Apply constraints to the type parameters.
        '
        ' A type that is substituted for the first parameter, TFirst,
        ' must be a reference type and must have a parameterless
        ' constructor.
        TFirst.SetGenericParameterAttributes( _
            GenericParameterAttributes.DefaultConstructorConstraint _
            Or GenericParameterAttributes.ReferenceTypeConstraint)

        ' A type that is substituted for the second type
        ' parameter must implement IExampleA and IExampleB, and
        ' inherit from the trivial test class ExampleBase. The
        ' interface constraints are specified as an array 
        ' containing the interface types.
        TSecond.SetBaseTypeConstraint(baseType)
        Dim interfaceTypes() As Type = {interfaceA, interfaceB}
        TSecond.SetInterfaceConstraints(interfaceTypes)

        ' The following code adds a private field named ExampleField,
        ' of type TFirst.
        Dim exField As FieldBuilder = _
            myType.DefineField("ExampleField", TFirst, _
                FieldAttributes.Private)

        ' Define a Shared method that takes an array of TFirst and 
        ' returns a List(Of TFirst) containing all the elements of 
        ' the array. To define this method it is necessary to create
        ' the type List(Of TFirst) by calling MakeGenericType on the
        ' generic type definition, List(Of T). (The T is omitted with
        ' the GetType operator when you get the generic type 
        ' definition.) The parameter type is created by using the
        ' MakeArrayType method. 
        '
        Dim listOf As Type = GetType(List(Of ))
        Dim listOfTFirst As Type = listOf.MakeGenericType(TFirst)
        Dim mParamTypes() As Type = { TFirst.MakeArrayType() }

        Dim exMethod As MethodBuilder = _
            myType.DefineMethod("ExampleMethod", _
                MethodAttributes.Public Or MethodAttributes.Static, _
                listOfTFirst, _
                mParamTypes)

        ' Emit the method body. 
        ' The method body consists of just three opcodes, to load 
        ' the input array onto the execution stack, to call the 
        ' List(Of TFirst) constructor that takes IEnumerable(Of TFirst),
        ' which does all the work of putting the input elements into
        ' the list, and to return, leaving the list on the stack. The
        ' hard work is getting the constructor.
        ' 
        ' The GetConstructor method is not supported on a 
        ' GenericTypeParameterBuilder, so it is not possible to get 
        ' the constructor of List(Of TFirst) directly. There are two
        ' steps, first getting the constructor of List(Of T) and then
        ' calling a method that converts it to the corresponding 
        ' constructor of List(Of TFirst).
        '
        ' The constructor needed here is the one that takes an
        ' IEnumerable(Of T). Note, however, that this is not the 
        ' generic type definition of IEnumerable(Of T); instead, the
        ' T from List(Of T) must be substituted for the T of 
        ' IEnumerable(Of T). (This seems confusing only because both
        ' types have type parameters named T. That is why this example
        ' uses the somewhat silly names TFirst and TSecond.) To get
        ' the type of the constructor argument, take the generic
        ' type definition IEnumerable(Of T) (expressed as 
        ' IEnumerable(Of ) when you use the GetType operator) and 
        ' call MakeGenericType with the first generic type parameter
        ' of List(Of T). The constructor argument list must be passed
        ' as an array, with just one argument in this case.
        ' 
        ' Now it is possible to get the constructor of List(Of T),
        ' using GetConstructor on the generic type definition. To get
        ' the constructor of List(Of TFirst), pass List(Of TFirst) and
        ' the constructor from List(Of T) to the static
        ' TypeBuilder.GetConstructor method.
        '
        Dim ilgen As ILGenerator = exMethod.GetILGenerator()
        
        Dim ienumOf As Type = GetType(IEnumerable(Of ))
        Dim listOfTParams() As Type = listOf.GetGenericArguments()
        Dim TfromListOf As Type = listOfTParams(0)
        Dim ienumOfT As Type = ienumOf.MakeGenericType(TfromListOf)
        Dim ctorArgs() As Type = { ienumOfT }

        Dim ctorPrep As ConstructorInfo = _
            listOf.GetConstructor(ctorArgs)
        Dim ctor As ConstructorInfo = _
            TypeBuilder.GetConstructor(listOfTFirst, ctorPrep)

        ilgen.Emit(OpCodes.Ldarg_0)
        ilgen.Emit(OpCodes.Newobj, ctor)
        ilgen.Emit(OpCodes.Ret)

        ' Create the type and save the assembly. 
        Dim finished As Type = myType.CreateType()
        myAssembly.Save(myAsmName.Name & ".dll")

        ' Invoke the method.
        ' ExampleMethod is not generic, but the type it belongs to is
        ' generic, so in order to get a MethodInfo that can be invoked
        ' it is necessary to create a constructed type. The Example 
        ' class satisfies the constraints on TFirst, because it is a 
        ' reference type and has a default constructor. In order to
        ' have a class that satisfies the constraints on TSecond, 
        ' this code example defines the ExampleDerived type. These
        ' two types are passed to MakeGenericMethod to create the
        ' constructed type.
        '
        Dim typeArgs() As Type = _
            { GetType(Example), GetType(ExampleDerived) }
        Dim constructed As Type = finished.MakeGenericType(typeArgs)
        Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")

        ' Create an array of Example objects, as input to the generic
        ' method. This array must be passed as the only element of an 
        ' array of arguments. The first argument of Invoke is 
        ' Nothing, because ExampleMethod is Shared. Display the count
        ' on the resulting List(Of Example).
        ' 
        Dim input() As Example = { New Example(), New Example() }
        Dim arguments() As Object = { input }

        Dim listX As List(Of Example) = mi.Invoke(Nothing, arguments)

        Console.WriteLine(vbLf & _
            "There are {0} elements in the List(Of Example).", _
            listX.Count _ 
        )

        DisplayGenericParameters(finished)
    End Sub

    Private Shared Sub DisplayGenericParameters(ByVal t As Type)

        If Not t.IsGenericType Then
            Console.WriteLine("Type '{0}' is not generic.")
            Return
        End If
        If Not t.IsGenericTypeDefinition Then _
            t = t.GetGenericTypeDefinition()

        Dim typeParameters() As Type = t.GetGenericArguments()
        Console.WriteLine(vbCrLf & _
            "Listing {0} type parameters for type '{1}'.", _
            typeParameters.Length, t)

        For Each tParam As Type In typeParameters

            Console.WriteLine(vbCrLf & "Type parameter {0}:", _
                tParam.ToString())

            For Each c As Type In tParam.GetGenericParameterConstraints()
                If c.IsInterface Then
                    Console.WriteLine("    Interface constraint: {0}", c)
                Else
                    Console.WriteLine("    Base type constraint: {0}", c)
                End If
            Next 

            ListConstraintAttributes(tParam)
        Next tParam
    End Sub

    ' List the constraint flags. The GenericParameterAttributes
    ' enumeration contains two sets of attributes, variance and
    ' constraints. For this example, only constraints are used.
    '
    Private Shared Sub ListConstraintAttributes(ByVal t As Type)

        ' Mask off the constraint flags. 
        Dim constraints As GenericParameterAttributes = _
            t.GenericParameterAttributes And _
            GenericParameterAttributes.SpecialConstraintMask

        If (constraints And GenericParameterAttributes.ReferenceTypeConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    ReferenceTypeConstraint")

        If (constraints And GenericParameterAttributes.NotNullableValueTypeConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    NotNullableValueTypeConstraint")

        If (constraints And GenericParameterAttributes.DefaultConstructorConstraint) _
                <> GenericParameterAttributes.None Then _
            Console.WriteLine("    DefaultConstructorConstraint")

    End Sub 

End Class

' This code example produces the following output:
'
'Type 'Sample' is generic: False
'Type 'Sample' is generic: True
'
'There are 2 elements in the List(Of Example).
'
'Listing 2 type parameters for type 'Sample[TFirst,TSecond]'.
'
'Type parameter TFirst:
'    ReferenceTypeConstraint
'    DefaultConstructorConstraint
'
'Type parameter TSecond:
'    Interface constraint: IExampleA
'    Interface constraint: IExampleB
'    Base type constraint: ExampleBase

注解

特殊约束可以指定分配给泛型类型参数的任何类型都必须具有无参数构造函数、必须是引用类型或必须是值类型。

无法使用 类的方法 GenericTypeParameterBuilder 检索特殊约束。 创建包含类型参数的泛型类型后,可以使用其 Type 对象来反映类型参数及其约束。 若要获取已完成泛型类型的类型参数,请使用 Type.GetGenericArguments 方法。 若要获取每个类型参数的特殊约束,请使用 Type.GenericParameterAttributes 属性。

GenericParameterAttributes引用类型参数方差特征的枚举值仅在支持协变和逆变的语言(例如 Microsoft 中间语言 (MSIL) )中相关。 Visual Basic 和 C# 当前不支持协变和逆变。

适用于

另请参阅