다음을 통해 공유


방법: 리플렉션 내보내기를 사용하여 제네릭 형식 정의

이 항목에서는 두 개의 매개 변수를 가진 간단한 제네릭 형식을 만드는 방법, 형식 매개 변수에 클래스 제약 조건, 인터페이스 제약 조건 및 특수 제약 조건을 적용하는 방법 및 클래스의 형식 매개 변수를 매개 변수 형식으로 사용하고 형식을 반환하는 멤버를 만드는 방법을 보여 줍니다.

중요중요

메서드는 제네릭 형식에 속하고 해당 형식의 형식 매개 변수를 사용하므로 제네릭이 아닙니다.메서드는 고유한 형식 매개 변수 목록이 있어야만 제네릭입니다.제네릭 형식의 대부분의 메서드는 다음 예제와 같이 제네릭이 아닙니다.제네릭 메서드 내보내기에 대한 예제를 보려면 방법: 리플렉션 내보내기를 사용하여 제네릭 메서드 정의를 참조하십시오.

제네릭 형식을 정의하려면

  1. GenericEmitExample1이라는 이름의 동적 어셈블리를 정의합니다. 이 예제에서는 어셈블리가 실행되고 디스크에 저장되므로 AssemblyBuilderAccess.RunAndSave가 지정됩니다.

    Dim myDomain As AppDomain = AppDomain.CurrentDomain
    Dim myAsmName As New AssemblyName("GenericEmitExample1")
    Dim myAssembly As AssemblyBuilder = myDomain.DefineDynamicAssembly( _
        myAsmName, _
        AssemblyBuilderAccess.RunAndSave)
    
    AppDomain myDomain = AppDomain.CurrentDomain;
    AssemblyName myAsmName = new AssemblyName("GenericEmitExample1");
    AssemblyBuilder myAssembly = 
        myDomain.DefineDynamicAssembly(myAsmName, 
            AssemblyBuilderAccess.RunAndSave);
    
    AppDomain^ myDomain = AppDomain::CurrentDomain;
    AssemblyName^ myAsmName = gcnew AssemblyName( L"GenericEmitExample1" );
    AssemblyBuilder^ myAssembly = myDomain->DefineDynamicAssembly( 
        myAsmName, AssemblyBuilderAccess::RunAndSave );
    
  2. 동적 모듈을 정의합니다. 어셈블리는 실행 모듈로 구성되어 있습니다. 단일 모듈 어셈블리의 경우 모듈 이름은 어셈블리 이름과 같고 파일 이름은 해당 모듈 이름에 확장명이 추가된 이름입니다.

    Dim myModule As ModuleBuilder = myAssembly.DefineDynamicModule( _
        myAsmName.Name, _
        myAsmName.Name & ".dll")
    
    ModuleBuilder myModule = 
        myAssembly.DefineDynamicModule(myAsmName.Name, 
           myAsmName.Name + ".dll");
    
    ModuleBuilder^ myModule = myAssembly->DefineDynamicModule( 
        myAsmName->Name, String::Concat( myAsmName->Name, L".dll" ) );
    
  3. 클래스를 정의합니다. 이 예제에서 클래스 이름은 Sample로 지정됩니다.

    Dim myType As TypeBuilder = myModule.DefineType( _
        "Sample", _
        TypeAttributes.Public)
    
    TypeBuilder myType = 
        myModule.DefineType("Sample", TypeAttributes.Public);
    
    TypeBuilder^ myType = myModule->DefineType( L"Sample", 
        TypeAttributes::Public );
    
  4. 매개 변수 이름이 포함된 문자열 배열을 TypeBuilder.DefineGenericParameters 메서드로 전달하여 Sample의 제네릭 형식 매개 변수를 정의합니다. 이렇게 하면 클래스가 제네릭 형식이 됩니다. 반환 값은 형식 매개 변수를 나타내는 GenericTypeParameterBuilder 개체의 배열이며 내보낸 코드에서 사용할 수 있습니다.

    다음 코드에서 Sample은 TFirst 및 TSecond 형식 매개 변수를 가진 제네릭 형식이 됩니다. 코드를 읽기 쉽게 만들기 위해 형식 매개 변수와 같은 이름을 사용하여 각 GenericTypeParameterBuilder를 변수에 배치합니다.

    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)
    
    string[] typeParamNames = {"TFirst", "TSecond"};
    GenericTypeParameterBuilder[] typeParams = 
        myType.DefineGenericParameters(typeParamNames);
    
    GenericTypeParameterBuilder TFirst = typeParams[0];
    GenericTypeParameterBuilder TSecond = typeParams[1];
    
    array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
    array<GenericTypeParameterBuilder^>^typeParams = 
        myType->DefineGenericParameters( typeParamNames );
    
    GenericTypeParameterBuilder^ TFirst = typeParams[0];
    GenericTypeParameterBuilder^ TSecond = typeParams[1];
    
  5. 형식 매개 변수에 특수 제약 조건을 추가합니다. 이 예제에서 형식 매개 변수 TFirst는 매개 변수가 없는 생성자를 가진 형식 및 참조 형식으로 제한됩니다.

    TFirst.SetGenericParameterAttributes( _
        GenericParameterAttributes.DefaultConstructorConstraint _
        Or GenericParameterAttributes.ReferenceTypeConstraint)
    
    TFirst.SetGenericParameterAttributes(
        GenericParameterAttributes.DefaultConstructorConstraint |
        GenericParameterAttributes.ReferenceTypeConstraint);
    
    TFirst->SetGenericParameterAttributes( 
        GenericParameterAttributes::DefaultConstructorConstraint | 
        GenericParameterAttributes::ReferenceTypeConstraint 
    );
    
  6. 형식 매개 변수에 클래스 제약 조건과 인터페이스 제약 조건을 선택적으로 추가합니다. 이 예제에서 형식 매개 변수 TFirst는 baseType 변수에 포함된 Type 개체가 나타내는 기본 클래스에서 파생되는 형식과, 해당 형식이 interfaceA 변수와 interfaceB 변수에 포함된 인터페이스를 구현하는 형식으로 제한됩니다. 이러한 변수의 선언 및 할당에 대해서는 코드 예제를 참조하십시오.

    TSecond.SetBaseTypeConstraint(baseType)
    Dim interfaceTypes() As Type = {interfaceA, interfaceB}
    TSecond.SetInterfaceConstraints(interfaceTypes)
    
    TSecond.SetBaseTypeConstraint(baseType);
    Type[] interfaceTypes = {interfaceA, interfaceB};
    TSecond.SetInterfaceConstraints(interfaceTypes);
    
    array<Type^>^interfaceTypes = { interfaceA, interfaceB };
    TSecond->SetInterfaceConstraints( interfaceTypes );
    TSecond->SetBaseTypeConstraint( baseType );
    
  7. 필드를 정의합니다. 이 예제에서 필드의 형식은 형식 매개 변수 TFirst에 의해 지정됩니다. GenericTypeParameterBuilderType에서 파생되므로 형식을 사용할 수 있는 모든 경우에 제네릭 형식 매개 변수를 사용할 수 있습니다.

    Dim exField As FieldBuilder = _
        myType.DefineField("ExampleField", TFirst, _
            FieldAttributes.Private)
    
    FieldBuilder exField = 
        myType.DefineField("ExampleField", TFirst, 
            FieldAttributes.Private);
    
    FieldBuilder^ exField = 
        myType->DefineField("ExampleField", TFirst, 
            FieldAttributes::Private);
    
  8. 제네릭 형식의 형식 매개 변수를 사용하는 메서드를 정의합니다. 이러한 매개 변수는 고유한 형식 매개 변수 목록이 없으면 제네릭이 아닙니다. 다음 코드에서는 TFirst 배열을 사용하고 해당 배열의 모든 요소가 들어 있는 List<TFirst>(Visual Basic의 경우List(Of TFirst))를 반환하는 static 메서드(Visual Basic의 경우 Shared)를 정의합니다. 이 메서드를 정의하려면 제네릭 형식인 List<T>에서 MakeGenericType을 호출하여 List<TFirst> 형식을 만들어야 합니다. (typeof 연산자(Visual Basic의 경우 GetType)를 사용하여 제네릭 형식 정의를 가져오는 경우 T가 생략됩니다.) 이 매개 변수 형식은 MakeArrayType 메서드를 사용하여 만듭니다.

    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)
    
    Type listOf = typeof(List<>);
    Type listOfTFirst = listOf.MakeGenericType(TFirst);
    Type[] mParamTypes = {TFirst.MakeArrayType()};
    
    MethodBuilder exMethod = 
        myType.DefineMethod("ExampleMethod", 
            MethodAttributes.Public | MethodAttributes.Static, 
            listOfTFirst, 
            mParamTypes);
    
    Type^ listOf = List::typeid;
    Type^ listOfTFirst = listOf->MakeGenericType(TFirst);
    array<Type^>^ mParamTypes = { TFirst->MakeArrayType() };
    
    MethodBuilder^ exMethod = 
        myType->DefineMethod("ExampleMethod", 
            MethodAttributes::Public | MethodAttributes::Static, 
            listOfTFirst, 
            mParamTypes);
    
  9. 메서드 본문을 내보냅니다. 메서드 본문은 입력 배열을 스택으로 로드하고, IEnumerable<TFirst>(입력 요소를 목록에 넣는 작업을 모두 수행함)를 사용하는 List<TFirst> 생성자를 호출하고, 스택에 새 List<T> 개체를 남겨둔 상태로 반환하는 세 개의 opcode로 구성되어 있습니다. 이 코드를 내보내는 과정에서 어려운 부분은 생성자를 가져오는 것입니다.

    GetConstructor 메서드는 GenericTypeParameterBuilder에서 지원되지 않으므로 List<TFirst>의 생성자를 직접 가져올 수 없습니다. 우선 List<T> 제네릭 형식 정의의 생성자를 가져온 다음 List<TFirst>의 해당하는 생성자로 변환하는 메서드를 호출해야 합니다.

    이 코드 예제에 사용된 생성자는 IEnumerable<T>를 사용합니다. 그러나 이 생성자는 IEnumerable<T> 제네릭 인터페이스의 제네릭 형식 정의가 아닙니다. List<T>의 형식 매개 변수 T를 IEnumerable<T>의 형식 매개 변수 T 대신 사용해야 합니다. (이 방법은 양쪽 형식에 모두 T라는 이름의 형식 매개 변수가 있는 경우 혼란스러울 수 있습니다. 따라서 이 코드 예제에서는 TFirst 및 TSecond라는 이름을 사용합니다.) 생성자 인수의 형식을 가져오려면 제네릭 형식 정의 IEnumerable<T>를 사용하여 시작하고 List<T>의 첫째 제네릭 형식 매개 변수를 사용하여 MakeGenericType을 호출합니다. 생성자 인수 목록은 이 경우 하나의 인수를 가진 하나의 배열로 전달되어야 합니다.

    참고참고

    제네릭 형식 정의는 C#에서 IEnumerable<> 연산자를 사용하는 경우 typeof로 표현되거나, Visual Basic에서 GetType 연산자를 사용하는 경우 IEnumerable(Of )로 표현됩니다.

    이제 제네릭 형식 정의에서 GetConstructor를 호출하여 List<T>의 생성자를 가져올 수 있습니다. 이 생성자를 List<TFirst>의 해당하는 생성자로 변환하려면 List<TFirst> 및 해당 생성자를 List<T>에서 정적 TypeBuilder.GetConstructor(Type, ConstructorInfo) 메서드로 전달합니다.

    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)
    
    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);
    
    ILGenerator^ ilgen = exMethod->GetILGenerator();
    
    Type^ ienumOf = IEnumerable::typeid;
    Type^ TfromListOf = listOf->GetGenericArguments()[0];
    Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf);
    array<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);
    
  10. 형식을 만들고 파일을 저장합니다.

    Dim finished As Type = myType.CreateType()
    myAssembly.Save(myAsmName.Name & ".dll")
    
    Type finished = myType.CreateType();
    myAssembly.Save(myAsmName.Name+".dll");
    
    Type^ finished = myType->CreateType();
    myAssembly->Save( String::Concat( myAsmName->Name, L".dll" ) );
    
  11. 메서드를 호출합니다. ExampleMethod는 제네릭이 아니지만 이 메서드가 속하는 형식은 제네릭이므로 호출할 수 있는 MethodInfo를 가져오려면 형식 정의에서 Sample에 대한 생성된 형식을 만들어야 합니다. 생성된 형식은, 참조 형식이고 기본 매개 변수가 없는 생성자가 있기 때문에 TFirst에 대한 제약 조건을 충족시키는 Example 클래스와 TSecond에 대한 제약 조건을 충족시키는 ExampleDerived 클래스를 사용합니다. (ExampleDerived의 코드는 예제 코드 섹션에서 찾을 수 있습니다.) 이러한 두 형식을 MakeGenericType으로 전달하여 생성된 형식을 만듭니다. GetMethod 메서드를 사용하여 MethodInfo를 가져옵니다.

    Dim typeArgs() As Type = _
        { GetType(Example), GetType(ExampleDerived) }
    Dim constructed As Type = finished.MakeGenericType(typeArgs)
    Dim mi As MethodInfo = constructed.GetMethod("ExampleMethod")
    
    Type[] typeArgs = {typeof(Example), typeof(ExampleDerived)};
    Type constructed = finished.MakeGenericType(typeArgs);
    MethodInfo mi = constructed.GetMethod("ExampleMethod");
    
    array<Type^>^ typeArgs = 
        { Example::typeid, ExampleDerived::typeid };
    Type^ constructed = finished->MakeGenericType(typeArgs);
    MethodInfo^ mi = constructed->GetMethod("ExampleMethod");
    
  12. 다음 코드에서는 Example 개체의 배열을 만들고, 해당 배열을 Object 형식의 배열에 배치하여 호출할 메서드의 인수를 나타낸 다음 Invoke(Object, Object[]) 메서드로 전달합니다. Invoke 메서드가 static이므로 해당 메서드의 첫째 인수는 null 참조입니다.

    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 _ 
    )
    
    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);
    
    array<Example^>^ input = { gcnew Example(), gcnew Example() };
    array<Object^>^ arguments = { input };
    
    List<Example^>^ listX = 
        (List<Example^>^) mi->Invoke(nullptr, arguments);
    
    Console::WriteLine(
        "\nThere are {0} elements in the List<Example>.", 
        listX->Count);
    

예제

다음 코드 예제에서는 기본 클래스 및 두 개의 인터페이스와 함께 Sample이라는 이름의 클래스를 정의합니다. 프로그램은 Sample에 대한 두 개의 제네릭 형식 매개 변수를 정의하고 이 클래스를 제네릭 형식으로 변환합니다. 형식 매개 변수만이 제네릭 형식을 만들 수 있습니다. 프로그램에서는 형식 매개 변수의 정의 전과 후의 테스트 메시지를 표시하여 이를 보여 줍니다.

형식 매개 변수 TSecond는 기본 클래스와 인터페이스를 사용하여 클래스 제약 조건과 인터페이스 제약 조건을 설명하는 데 사용되고, 형식 매개 변수 TFirst는 특수 제약 조건을 설명하는 데 사용됩니다.

이 코드 예제에서는 필드 형식과 매개 변수에 대한 클래스의 형식 매개 변수와 메서드의 반환 형식을 사용하여 필드와 메서드를 정의합니다.

Sample 클래스가 만들어진 후에 메서드가 호출됩니다.

프로그램에는 제네릭 형식에 대한 정보를 나열하는 메서드와 형식 매개 변수에 대한 특수 제약 조건을 나열하는 메서드가 포함됩니다. 이러한 메서드는 완료된 Sample 클래스에 대한 정보를 표시하는 데 사용됩니다.

프로그램에서 완료된 모듈을 디스크에 GenericEmitExample1.dll로 저장하므로 Ildasm.exe(MSIL 디스어셈블러)를 사용하여 이 파일을 열고 Sample 클래스에 대한 MSIL을 검사합니다.

Imports System
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
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
 */
using namespace System;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
using namespace System::Collections::Generic;

// Dummy class to satisfy TFirst constraints.
//
public ref class Example {};

// Define a trivial base class and two trivial interfaces 
// to use when demonstrating constraints.
//
public ref class ExampleBase {};
public interface class IExampleA {};
public interface class IExampleB {};

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

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

   if ((constraints & GenericParameterAttributes::ReferenceTypeConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    ReferenceTypeConstraint");

   if ((constraints & GenericParameterAttributes::NotNullableValueTypeConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    NotNullableValueTypeConstraint");

   if ((constraints & GenericParameterAttributes::DefaultConstructorConstraint)
           != GenericParameterAttributes::None)
       Console::WriteLine( L"    DefaultConstructorConstraint");
}

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

   array<Type^>^ typeParameters = t->GetGenericArguments();
   Console::WriteLine( L"\r\nListing {0} type parameters for type '{1}'.", 
       typeParameters->Length, t );

   for each ( Type^ tParam in typeParameters )
   {
       Console::WriteLine( L"\r\nType parameter {0}:", 
           tParam->ToString() );

       for each (Type^ c in tParam->GetGenericParameterConstraints())
       {
           if (c->IsInterface)
               Console::WriteLine( L"    Interface constraint: {0}", c);
           else
               Console::WriteLine( L"    Base type constraint: {0}", c);
       }
       ListConstraintAttributes(tParam);
   }
}

void main()
{
   // Define a dynamic assembly to contain the sample type. The
   // assembly will be run and also saved to disk, so
   // AssemblyBuilderAccess.RunAndSave is specified.
   //
   AppDomain^ myDomain = AppDomain::CurrentDomain;
   AssemblyName^ myAsmName = gcnew AssemblyName( L"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, String::Concat( myAsmName->Name, L".dll" ) );

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

   // Define the sample type.
   //
   TypeBuilder^ myType = myModule->DefineType( L"Sample", 
       TypeAttributes::Public );

   Console::WriteLine( L"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.
   // 
   array<String^>^typeParamNames = {L"TFirst",L"TSecond"};
   array<GenericTypeParameterBuilder^>^typeParams = 
       myType->DefineGenericParameters( typeParamNames );

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

   Console::WriteLine( L"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. 
   array<Type^>^interfaceTypes = { interfaceA, interfaceB };
   TSecond->SetInterfaceConstraints( interfaceTypes );
   TSecond->SetBaseTypeConstraint( baseType );

   // 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, generic<T> List. 
   // The parameter type is created by using the
   // MakeArrayType method. 
   //
   Type^ listOf = List::typeid;
   Type^ listOfTFirst = listOf->MakeGenericType(TFirst);
   array<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 generic<T> List 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 generic<T> IEnumerable; instead, the
   // T from generic<T> List must be substituted for the T of 
   // generic<T> IEnumerable. (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 generic<T> IEnumerable and 
   // call MakeGenericType with the first generic type parameter
   // of generic<T> List. 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 generic<T> List,
   // using GetConstructor on the generic type definition. To get
   // the constructor of List<TFirst>, pass List<TFirst> and
   // the constructor from generic<T> List to the static
   // TypeBuilder.GetConstructor method.
   //
   ILGenerator^ ilgen = exMethod->GetILGenerator();

   Type^ ienumOf = IEnumerable::typeid;
   Type^ TfromListOf = listOf->GetGenericArguments()[0];
   Type^ ienumOfT = ienumOf->MakeGenericType(TfromListOf);
   array<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( String::Concat( myAsmName->Name, L".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.
   //
   array<Type^>^ typeArgs = 
       { Example::typeid, ExampleDerived::typeid };
   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>.
   // 
   array<Example^>^ input = { gcnew Example(), gcnew Example() };
   array<Object^>^ arguments = { input };

   List<Example^>^ listX = 
       (List<Example^>^) mi->Invoke(nullptr, arguments);

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

   DisplayGenericParameters(finished);
}

/* 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
 */

코드 컴파일

  • 이 코드에는 컴파일에 필요한 C# using 문(Visual Basic의 경우 Imports)이 포함되어 있습니다.

  • 추가 어셈블리 참조는 필요하지 않습니다.

  • csc.exe, vbc.exe 또는 cl.exe를 사용하여 명령줄에서 코드를 컴파일합니다. Visual Studio에서 코드를 컴파일하려면 콘솔 응용 프로젝트 템플릿에 코드를 삽입합니다.

참고 항목

참조

GenericTypeParameterBuilder

개념

리플렉션 내보내기 동적 어셈블리 시나리오

기타 리소스

리플렉션 내보내기 사용