回答:
ジェネリック型のインスタンスであるかどうかを確認する場合:
return list.GetType().IsGenericType;
それがジェネリックかどうかを確認したい場合List<T>
:
return list.GetType().GetGenericTypeDefinition() == typeof(List<>);
Jonが指摘するように、これは正確な型の等価性をチェックします。戻るとfalse
は、必ずしもlist is List<T>
戻ることを意味するわけではありませんfalse
(つまり、オブジェクトをList<T>
変数に割り当てることができません)。
型が総称かどうかを知りたいだけでなく、オブジェクトが特定の総称型のインスタンスである場合は、型引数を知らなくてもよいと思います。
残念ながら、それほど単純ではありません。(この場合のように)ジェネリック型がクラスである場合はそれほど悪くはありませんが、インターフェイスでは難しくなります。これがクラスのコードです:
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;
}
}
私はこっそりと疑いを抱いていますが、これについては厄介な問題があるかもしれませんが、今のところ失敗するケースは見つかりません。
List<T>
、何らかの形でヒットするかどうかを確認できます。インターフェースを含めると、それは本当にトリッキーです。
IsInstanceOfGenericType
呼び出しで置き換えることができませんか?IsAssignableFrom
==
動的アルゴリズムを使用して短いコードを使用できますが、これは純粋なリフレクションよりも遅くなる可能性があります。
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();
これらは、ジェネリック型チェックのほとんどのエッジケースをカバーする私の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);
}
return list.GetType().IsGenericType;