web-dev-qa-db-ja.com

C#でオブジェクトがジェネリック型かどうかをテストする

オブジェクトがジェネリック型である場合、テストを実行したいと思います。私は成功せずに次のことを試しました:

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

私は何を間違えていますか?このテストはどのように実行しますか?

122
Richbits

ジェネリック型のインスタンスかどうかを確認する場合:

return list.GetType().IsGenericType;

汎用List<T>かどうかを確認する場合:

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

Jonが指摘しているように、これは正確な型の等価性をチェックします。 falseを返すことは、必ずしもlist is List<T>falseを返すことを意味しません(つまり、オブジェクトをList<T>変数に割り当てることはできません)。

179
Mehrdad Afshari

型がジェネリックであるかどうかだけを知りたいのではなく、オブジェクトが特定のジェネリック型のインスタンスである場合、型引数を知らなくてもよいと思います。

残念ながら、それほど単純ではありません。ジェネリック型がクラスの場合(この場合のように)それほど悪くはありませんが、インターフェイスにとってはより困難です。クラスのコードは次のとおりです。

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>
    {
    }
}

編集:コメントで述べたように、これはインターフェイスで機能する場合があります:

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

私はこっそりと疑いを持っていますが、この周りにいくつかの厄介なエッジのケースがあるかもしれませんが、今のところ失敗するものを見つけることができません。

81
Jon Skeet

動的なアルゴリズムを使用して短いコードを使用できますが、これは純粋なリフレクションよりも遅い場合があります。

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

これらは、ジェネリック型チェックのほとんどのEdgeのケースをカバーする私のお気に入りの拡張メソッドです。

で動作します:

  • 複数の(汎用)インターフェイス
  • 複数の(汎用)基本クラス
  • Trueを返す場合、特定のジェネリック型を 'out'するオーバーロードがあります(サンプルのユニットテストを参照)。

    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;
        }
    }
    

(基本)機能を実証するためのテストを次に示します。

 [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.