共用方式為


如何:使用反射檢測和具現化泛型型別

泛型型別的相關信息與其他類型的資訊相同:藉由檢查代表泛型型別的 Type 物件。 主要差異在於泛型類型具有代表其泛型型別參數的 Type 對象清單。 本節中的第一個程式會檢查泛型類型。

您可以建立 Type 物件,將型別自變數系結至泛型型別定義的型別參數,以代表建構型別。 第二個程序會示範這一點。

檢查泛型類型及其型別參數

  1. 取得代表泛型型別的 Type 實例。 在下列程式代碼中,類型是使用 C# typeof 運算子取得的(在 Visual Basic 中GetType)。 如需取得 Type 物件的其他方式,請參閱 Type。 在此程序的其餘部分,類型會包含在名為 t的方法參數中。

    Type d1 = typeof(Dictionary<,>);
    
    Dim d1 As Type = GetType(Dictionary(Of ,))
    
  2. 使用 IsGenericType 屬性來判斷類型是否為泛型,並使用 IsGenericTypeDefinition 屬性來判斷類型是否為泛型型別定義。

    Console.WriteLine($"   Is this a generic type? {t.IsGenericType}");
    Console.WriteLine($"   Is this a generic type definition? {t.IsGenericTypeDefinition}");
    
    Console.WriteLine("   Is this a generic type? " _
        & t.IsGenericType)
    Console.WriteLine("   Is this a generic type definition? " _
        & t.IsGenericTypeDefinition)
    
  3. 使用 GetGenericArguments 方法,取得包含泛型型別自變數的陣列。

    Type[] typeParameters = t.GetGenericArguments();
    
    Dim typeParameters() As Type = t.GetGenericArguments()
    
  4. 針對每個類型自變數,使用 IsGenericParameter 屬性,判斷它是類型參數(例如,在泛型型別定義中),還是為類型參數指定的類型(例如,在建構型別中)。

    Console.WriteLine($"   List {typeParameters.Length} type arguments:");
    foreach (Type tParam in typeParameters)
    {
        if (tParam.IsGenericParameter)
        {
            DisplayGenericParameter(tParam);
        }
        else
        {
            Console.WriteLine($"      Type argument: {tParam}");
        }
    }
    
    Console.WriteLine("   List {0} type arguments:", _
        typeParameters.Length)
    For Each tParam As Type In typeParameters
        If tParam.IsGenericParameter Then
            DisplayGenericParameter(tParam)
        Else
            Console.WriteLine("      Type argument: {0}", _
                tParam)
        End If
    Next
    
  5. 在類型系統中,泛型型別參數是由 Type實例表示,就像一般類型一樣。 下列程式代碼會顯示代表泛型型別參數之 Type 對象的名稱和參數位置。 參數位置在這裡是次要的資訊;當您檢查已作為另一個泛型類型之類型引數的類型參數時,這會更令人感興趣。

    Console.WriteLine($"      Type parameter: {tp.Name} position {tp.GenericParameterPosition}");
    
    Private Shared Sub DisplayGenericParameter(ByVal tp As Type)
        Console.WriteLine("      Type parameter: {0} position {1}", _
            tp.Name, tp.GenericParameterPosition)
    
  6. 使用 GetGenericParameterConstraints 方法來獲取所有約束條件於一個單一陣列中,以判斷泛型型別參數的基底型別約束和介面約束。 條件約束不保證會以任何特定順序排列。

    foreach (Type iConstraint in tp.GetGenericParameterConstraints())
    {
        if (iConstraint.IsInterface)
        {
            Console.WriteLine($"         Interface constraint: {iConstraint}");
        }
    }
    
    Console.WriteLine($"         Base type constraint: {tp.BaseType ?? tp.BaseType: None}");
    
    Dim classConstraint As Type = Nothing
    
    For Each iConstraint As Type In tp.GetGenericParameterConstraints()
        If iConstraint.IsInterface Then
            Console.WriteLine("         Interface constraint: {0}", _
                iConstraint)
        End If
    Next
    
    If classConstraint IsNot Nothing Then
        Console.WriteLine("         Base type constraint: {0}", _
            tp.BaseType)
    Else
        Console.WriteLine("         Base type constraint: None")
    End If
    
  7. 使用 GenericParameterAttributes 屬性來探索類型參數的特殊條件約束,例如要求它是參考型別。 屬性也包含代表變異數的值,您可以遮罩,如下列程式代碼所示。

    GenericParameterAttributes sConstraints =
        tp.GenericParameterAttributes &
        GenericParameterAttributes.SpecialConstraintMask;
    
    Dim sConstraints As GenericParameterAttributes = _
        tp.GenericParameterAttributes And _
        GenericParameterAttributes.SpecialConstraintMask
    
  8. 特殊條件約束屬性是旗標,而代表沒有特殊條件約束的相同旗標GenericParameterAttributes.None也代表無共變數或反變數。 因此,若要測試上述任一條件,您必須使用適當的遮罩。 在此情況下,請使用 GenericParameterAttributes.SpecialConstraintMask 來隔離特殊條件約束旗標。

    if (sConstraints == GenericParameterAttributes.None)
    {
        Console.WriteLine("         No special constraints.");
    }
    else
    {
        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.");
        }
    }
    
    If sConstraints = GenericParameterAttributes.None Then
        Console.WriteLine("         No special constraints.")
    Else
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.DefaultConstructorConstraint) Then
            Console.WriteLine("         Must have a parameterless constructor.")
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.ReferenceTypeConstraint) Then
            Console.WriteLine("         Must be a reference type.")
        End If
        If GenericParameterAttributes.None <> (sConstraints And _
            GenericParameterAttributes.NotNullableValueTypeConstraint) Then
            Console.WriteLine("         Must be a non-nullable value type.")
        End If
    End If
    

建構泛型類型的實例

泛型類型就像範本。 除非指定其泛型型別參數的實際類型,否則您無法建立它的實例。 若要在運行時間執行這項作業,使用反射需要 MakeGenericType 方法。

  1. 取得代表泛型類型的 Type 物件。 下列程式代碼會以兩種不同的方式取得泛型類型 Dictionary<TKey,TValue>:使用 Type.GetType(String) 方法多載搭配描述型別的字串,以及在建構型別 GetGenericTypeDefinition 上呼叫 Dictionary\<String, Example> 方法(在 Visual Basic 中為Dictionary(Of String, Example))。 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 = [];
    // 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();
    
    ' Use the GetType 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.
    Dim d1 As Type = GetType(Dictionary(Of ,))
    
    ' 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.
    Dim d2 As New Dictionary(Of 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.
    Dim d3 As Type = d2.GetType()
    Dim d4 As Type = d3.GetGenericTypeDefinition()
    
  2. 建構型別自變數陣列,以取代型別參數。 陣列必須包含正確的 Type 物件數目,順序與類型參數清單中顯示的順序相同。 在此情況下,索引鍵 (第一個類型參數) 的類型為 String,而字典中的值是名為 Example類別的實例。

    Type[] typeArgs = [typeof(string), typeof(Example)];
    
    Dim typeArgs() As Type = _
        {GetType(String), GetType(Example)}
    
  3. 呼叫 MakeGenericType 方法,將型別自變數系結至型別參數並建構型別。

    Type constructed = d1.MakeGenericType(typeArgs);
    
    Dim constructed As Type = _
        d1.MakeGenericType(typeArgs)
    
  4. 使用 CreateInstance(Type) 方法重載來建立建構型別的物件。 下列程式代碼會將 Example 類別的兩個實例儲存在產生的 Dictionary<String, Example> 物件中。

    _ = Activator.CreateInstance(constructed);
    
    Dim o As Object = Activator.CreateInstance(constructed)
    

範例

下列程式代碼範例會定義 DisplayGenericType 方法來檢查程式代碼中使用的泛型型別定義和建構型別,並顯示其資訊。 DisplayGenericType 方法示範如何使用 IsGenericTypeIsGenericParameterGenericParameterPosition 屬性和 GetGenericArguments 方法。

此範例也會定義 DisplayGenericParameter 方法來檢查泛型型別參數並顯示其條件約束。

程式代碼範例會定義一組測試類型,包括說明類型參數條件約束的泛型型別,並示範如何顯示這些類型的相關信息。

此範例會藉由建立類型自變數的陣列並呼叫 Dictionary<TKey,TValue> 方法,從 MakeGenericType 類別建構類型。 程序會比較使用 Type 建構的 MakeGenericType 物件,以及使用Visual Basic Type 取得的 typeof 物件(在Visual Basic中GetType),示範它們相同。 同樣地,程式會使用 GetGenericTypeDefinition 方法來取得建構型別的泛型型別定義,並將它與代表 Type 類別的 Dictionary<TKey,TValue> 對象進行比較。

using System.Reflection;

// 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 {t}");
        Console.WriteLine($"   Is this a generic type? {t.IsGenericType}");
        Console.WriteLine($"   Is this a generic type definition? {t.IsGenericTypeDefinition}");

        // Get the generic type parameters or type arguments.
        Type[] typeParameters = t.GetGenericArguments();

        Console.WriteLine($"   List {typeParameters.Length} type arguments:");
        foreach (Type tParam in typeParameters)
        {
            if (tParam.IsGenericParameter)
            {
                DisplayGenericParameter(tParam);
            }
            else
            {
                Console.WriteLine($"      Type argument: {tParam}");
            }
        }
    }

    // Displays information about a generic type parameter.
    private static void DisplayGenericParameter(Type tp)
    {
        Console.WriteLine($"      Type parameter: {tp.Name} position {tp.GenericParameterPosition}");

        foreach (Type iConstraint in tp.GetGenericParameterConstraints())
        {
            if (iConstraint.IsInterface)
            {
                Console.WriteLine($"         Interface constraint: {iConstraint}");
            }
        }

        Console.WriteLine($"         Base type constraint: {tp.BaseType ?? tp.BaseType: None}");

        GenericParameterAttributes sConstraints =
            tp.GenericParameterAttributes &
            GenericParameterAttributes.SpecialConstraintMask;

        if (sConstraints == GenericParameterAttributes.None)
        {
            Console.WriteLine("         No special constraints.");
        }
        else
        {
            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.");
            }
        }
    }

    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 = [];
        // 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);
        _ = Activator.CreateInstance(constructed);

        Console.WriteLine("\r\nCompare types obtained by different methods:");
        Console.WriteLine($"   Are the constructed types equal? {d2.GetType() == constructed}");
        Console.WriteLine($"   Are the generic definitions equal? {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<>));
    }
}
Imports System.Reflection
Imports System.Collections.Generic

' Define an example interface.
Public Interface ITestArgument
End Interface

' Define an example base class.
Public Class TestBase
End Class

' 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(Of T As {TestBase, ITestArgument, New})
End Class

' Define a class that meets the constraints on the type
' parameter of class Test.
Public Class TestArgument
    Inherits TestBase
    Implements ITestArgument
    Public Sub New()
    End Sub
End Class

Public Class Example
    ' The following method displays information about a generic
    ' type.
    Private Shared Sub DisplayGenericType(ByVal t As Type)
        Console.WriteLine(vbCrLf & t.ToString())
        Console.WriteLine("   Is this a generic type? " _
            & t.IsGenericType)
        Console.WriteLine("   Is this a generic type definition? " _
            & t.IsGenericTypeDefinition)

        ' Get the generic type parameters or type arguments.
        Dim typeParameters() As Type = t.GetGenericArguments()

        Console.WriteLine("   List {0} type arguments:", _
            typeParameters.Length)
        For Each tParam As Type In typeParameters
            If tParam.IsGenericParameter Then
                DisplayGenericParameter(tParam)
            Else
                Console.WriteLine("      Type argument: {0}", _
                    tParam)
            End If
        Next
    End Sub

    ' 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 Shared Sub DisplayGenericParameter(ByVal tp As Type)
        Console.WriteLine("      Type parameter: {0} position {1}", _
            tp.Name, tp.GenericParameterPosition)

        Dim classConstraint As Type = Nothing

        For Each iConstraint As Type In tp.GetGenericParameterConstraints()
            If iConstraint.IsInterface Then
                Console.WriteLine("         Interface constraint: {0}", _
                    iConstraint)
            End If
        Next

        If classConstraint IsNot Nothing Then
            Console.WriteLine("         Base type constraint: {0}", _
                tp.BaseType)
        Else
            Console.WriteLine("         Base type constraint: None")
        End If

        Dim sConstraints As GenericParameterAttributes = _
            tp.GenericParameterAttributes And _
            GenericParameterAttributes.SpecialConstraintMask
        If sConstraints = GenericParameterAttributes.None Then
            Console.WriteLine("         No special constraints.")
        Else
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.DefaultConstructorConstraint) Then
                Console.WriteLine("         Must have a parameterless constructor.")
            End If
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.ReferenceTypeConstraint) Then
                Console.WriteLine("         Must be a reference type.")
            End If
            If GenericParameterAttributes.None <> (sConstraints And _
                GenericParameterAttributes.NotNullableValueTypeConstraint) Then
                Console.WriteLine("         Must be a non-nullable value type.")
            End If
        End If
    End Sub

    Public Shared Sub Main()
        ' Two ways to get a Type object that represents the generic
        ' type definition of the Dictionary class. 
        '
        ' Use the GetType 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.
        Dim d1 As Type = GetType(Dictionary(Of ,))

        ' 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.
        Dim d2 As New Dictionary(Of 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.
        Dim d3 As Type = d2.GetType()
        Dim d4 As Type = d3.GetGenericTypeDefinition()

        ' Display information for the generic type definition, and
        ' for the constructed type Dictionary(Of 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.
        Dim typeArgs() As Type = _
            {GetType(String), GetType(Example)}

        ' Construct the type Dictionary(Of String, Example).
        Dim constructed As Type = _
            d1.MakeGenericType(typeArgs)

        DisplayGenericType(constructed)

        Dim o As Object = Activator.CreateInstance(constructed)

        Console.WriteLine(vbCrLf & _
            "Compare types obtained by different methods:")
        Console.WriteLine("   Are the constructed types equal? " _
            & (d2.GetType() Is constructed))
        Console.WriteLine("   Are the generic definitions equal? " _
            & (d1 Is constructed.GetGenericTypeDefinition()))

        ' Demonstrate the DisplayGenericType and 
        ' DisplayGenericParameter methods with the Test class 
        ' defined above. This shows base, interface, and special
        ' constraints.
        DisplayGenericType(GetType(Test(Of )))
    End Sub
End Class

另請參閱