it-swarm.com.de

Testen, ob das Objekt in C # vom generischen Typ ist

Ich möchte einen Test durchführen, ob ein Objekt von einem generischen Typ ist. Ich habe Folgendes ohne Erfolg versucht:

public bool Test()
{
    List<int> list = new List<int>();
    return list.GetType() == typeof(List<>);
}

Was mache ich falsch und wie führe ich diesen Test durch?

122
Richbits

Wenn Sie überprüfen möchten, ob es sich um eine Instanz eines generischen Typs handelt:

return list.GetType().IsGenericType;

Wenn Sie überprüfen möchten, ob es sich um ein generisches List<T> Handelt:

return list.GetType().GetGenericTypeDefinition() == typeof(List<>);

Wie Jon betont, überprüft dies die genaue Typäquivalenz. Die Rückgabe von false bedeutet nicht unbedingt, dass list is List<T>false zurückgibt (d. H., Das Objekt kann keiner List<T> - Variablen zugewiesen werden).

179
Mehrdad Afshari

Ich gehe davon aus, dass Sie nicht nur wissen möchten, ob der Typ generisch ist, sondern ob ein Objekt eine Instanz eines bestimmten generischen Typs ist, ohne die Typargumente zu kennen.

Es ist leider nicht so einfach. Es ist nicht schlecht, wenn der generische Typ eine Klasse ist (wie in diesem Fall), aber es ist schwieriger für Schnittstellen. Hier ist der Code für eine Klasse:

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

class Test
{
    static bool IsInstanceOfGenericType(Type genericType, object instance)
    {
        Type type = instance.GetType();
        while (type != null)
        {
            if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == genericType)
            {
                return true;
            }
            type = type.BaseType;
        }
        return false;
    }

    static void Main(string[] args)
    {
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new List<string>()));
        // False
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new string[0]));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList()));
        // True
        Console.WriteLine(IsInstanceOfGenericType(typeof(List<>),
                                                  new SubList<int>()));
    }

    class SubList : List<string>
    {
    }

    class SubList<T> : List<T>
    {
    }
}

BEARBEITEN: Wie in Kommentaren vermerkt, kann dies für Schnittstellen funktionieren:

foreach (var i in type.GetInterfaces())
{
    if (i.IsGenericType && i.GetGenericTypeDefinition() == genericType)
    {
        return true;
    }
}

Ich habe den Verdacht, dass es einige unangenehme Edge-Fälle gibt, aber ich kann keinen finden, der im Moment fehlschlägt.

81
Jon Skeet

Sie können kürzeren Code mit dynamic verwenden, obwohl dies möglicherweise langsamer als die reine Reflektion ist:

public static class Extension
{
    public static bool IsGenericList(this object o)
    {
       return IsGeneric((dynamic)o);
    }

    public static bool IsGeneric<T>(List<T> o)
    {
       return true;
    }

    public static bool IsGeneric( object o)
    {
        return false;
    }
}



var l = new List<int>();
l.IsGenericList().Should().BeTrue();

var o = new object();
o.IsGenericList().Should().BeFalse();
6

Dies sind meine zwei bevorzugten Erweiterungsmethoden, die die meisten Edge-Fälle der allgemeinen Typprüfung abdecken:

Arbeitet mit:

  • Mehrere (generische) Schnittstellen
  • Mehrere (generische) Basisklassen
  • Hat eine Überladung, die den spezifischen generischen Typ "out", wenn es true zurückgibt (siehe Unit-Test für Beispiele):

    public static bool IsOfGenericType(this Type typeToCheck, Type genericType)
    {
        Type concreteType;
        return typeToCheck.IsOfGenericType(genericType, out concreteType); 
    }
    
    public static bool IsOfGenericType(this Type typeToCheck, Type genericType, out Type concreteGenericType)
    {
        while (true)
        {
            concreteGenericType = null;
    
            if (genericType == null)
                throw new ArgumentNullException(nameof(genericType));
    
            if (!genericType.IsGenericTypeDefinition)
                throw new ArgumentException("The definition needs to be a GenericTypeDefinition", nameof(genericType));
    
            if (typeToCheck == null || typeToCheck == typeof(object))
                return false;
    
            if (typeToCheck == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if ((typeToCheck.IsGenericType ? typeToCheck.GetGenericTypeDefinition() : typeToCheck) == genericType)
            {
                concreteGenericType = typeToCheck;
                return true;
            }
    
            if (genericType.IsInterface)
                foreach (var i in typeToCheck.GetInterfaces())
                    if (i.IsOfGenericType(genericType, out concreteGenericType))
                        return true;
    
            typeToCheck = typeToCheck.BaseType;
        }
    }
    

Hier ist ein Test, um die (grundlegende) Funktionalität zu demonstrieren:

 [Test]
    public void SimpleGenericInterfaces()
    {
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>)));
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>)));

        Type concreteType;
        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IEnumerable<>), out concreteType));
        Assert.AreEqual(typeof(IEnumerable<string>), concreteType);

        Assert.IsTrue(typeof(Table<string>).IsOfGenericType(typeof(IQueryable<>), out concreteType));
        Assert.AreEqual(typeof(IQueryable<string>), concreteType);


    }
5
Wiebe Tijsma
return list.GetType().IsGenericType;
0
Stan R.