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


134

オブジェクトがジェネリック型であるかどうかテストを実行したいと思います。私は成功せずに以下を試しました:

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

私は何が間違っているのですか、またこのテストをどのように実行しますか?

回答:


201

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

return list.GetType().IsGenericType;

それがジェネリックかどうかを確認したい場合List<T>

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

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


9
ただし、サブタイプは検出されません。私の答えを見てください。インターフェースの場合もはるかに困難です:(
Jon Skeet

1
GetGenericTypeDefinitionの呼び出しは、ジェネリック型でない場合にスローされます。最初に確認してください。
キルホッファー

85

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

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

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

私はこっそりと疑いを抱いていますが、これについては厄介な問題があるかもしれませんが、今のところ失敗するケースは見つかりません。


2
この問題を発見しました。継承は1行だけです。途中で、基本クラス探しているインターフェースの両方を備えたベースがある場合、これはクラスパスのみを下ります。
Groxx、2011年

1
@Groxx:はい。私は答えでそれを言及していることを発見しました:「ジェネリック型がクラスである場合(この場合のように)はそれほど悪くはありませんが、インターフェースにとっては難しいです。クラスのコードは次のとおりです」
ジョンスキート

1
<T>を知る方法がない場合はどうなりますか?同様に、それはintまたはstringかもしれませんが、あなたはそれを知りません。これは、偽陰性を生成するように見えます...使用するTがなく、オブジェクトのプロパティを調べているだけで、1つはリストです。それがリストから外れるように、それがどのようにしてわかるのですか?つまり、Tも使用する型もありません。すべてのタイプを推測できます(List <int>?はList <string>?)ですが、知りたいのはこれがAA LISTですか?その質問に答えるのは難しいようです。

@RiverC:はい、そうです。さまざまな理由から、答えるのかなり難しいです。クラスについて話しているだけなら、それほど悪くはありません...継承ツリーを上に向かって進みList<T>、何らかの形でヒットするかどうかを確認できます。インターフェースを含めると、それは本当にトリッキーです。
Jon Skeet 2013年

3
ループを等価演算子()の代わりにIsInstanceOfGenericType呼び出しで置き換えることができませんか?IsAssignableFrom==
slawekwin 2016

7

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

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();

7

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

で動作します:

  • 複数の(汎用)インターフェース
  • 複数の(汎用)基本クラス
  • trueが返された場合に特定のジェネリック型を「アウト」するオーバーロードがあります(サンプルの単体テストを参照):

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


    }

0
return list.GetType().IsGenericType;

3
別の質問に正解です。この質問については、問題の半分に(かなり少ない)しか対処していないため、これは誤りです。
Groxx 2011年

1
スタンRの答えは実際には提示された質問に答えますが、OPが実際に意味したのは、「オブジェクトがC#の特定のジェネリック型かどうかのテスト」でした。この答えは確かに不完全です。
yoyo

「is of」ジェネリック型ではなく「is a」ジェネリック型のコンテキストで質問に答えたので、人々は私に反対票を投じています。英語は私の第2言語であり、そのような言語のニュアンスはしばしば私を通り越します、私の防御のために、OPは特定のタイプに対してテストするように特に尋ねませんでした、そしてタイトルで "is of"ジェネリックタイプを尋ねます...あいまいな質問。
スタンR.

2
これであなたはそれを知っており、あなたはあなたの答えをより具体的で正しいものに改善することができます。
Peter Ivan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.