确定对象是否派生自集合类型

时间:2009-04-15 02:08:02

标签: c# .net reflection

我想确定通用对象类型(“T”)方法类型参数是否是集合类型。我通常会将T through作为Generic.List发送,但它可以是任何集合类型,因为它在辅助函数中使用。

我是否最好测试它是否实现IEnumerable< T>?

如果是这样,代码会是什么样的?

更新14:17 GMT + 10可能延伸到解决方案here(但是,如果列表派生的话,它只适用于List&lt; T&gt;不适用于IEnumerable&lt; T&gt;)< /强>

T currentObj;    
// works if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(List<>)
// does not work if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(IEnumerable<>)

11 个答案:

答案 0 :(得分:31)

这将是最简单的检查..

if(Obj is ICollection)
{
    //Derived from ICollection
}
else
{
    //Not Derived from ICollection
}

答案 1 :(得分:15)

你可以使用带有错位名称的Type.GetInterface()。

private bool IsTAnEnumerable<T>(T x)
{
    return null != typeof(T).GetInterface("IEnumerable`1");
}

答案 2 :(得分:8)

为了在运行时获取T的实际类型,可以使用typeof(T)表达式。从那里,普通类型比较运算符将起到作用

bool isEnumerable = typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));

完整代码示例:

static bool Foo<T>()
{
  return typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
}

Foo<List<T>>();  // true
Foo<int>(); // false

答案 3 :(得分:7)

我个人倾向于使用自己编写的方法,名为TryGetInterfaceGenericParameters,我在下面发布。以下是如何在您的情况下使用它:

使用示例

object currentObj = ...;  // get the object
Type[] typeArguments;
if (currentObj.GetType().TryGetInterfaceGenericParameters(typeof(IEnumerable<>), out typeArguments))
{
    var innerType = typeArguments[0];
    // currentObj implements IEnumerable<innerType>
}
else
{
    // The type does not implement IEnumerable<T> for any T
}

这里需要注意的是,您传递了typeof(IEnumerable<>)不是 typeof(IEnumerable)(这是一种完全不同的类型)以及任何typeof(IEnumerable<T>) T(如果您已经知道T,则不需要此方法)。当然,这适用于任何通用接口,例如您也可以使用typeof(IDictionary<,>)(但 typeof(IDictionary))。

方法来源

/// <summary>
///     Determines whether the current type is or implements the specified generic interface, and determines that
///     interface's generic type parameters.</summary>
/// <param name="type">
///     The current type.</param>
/// <param name="interface">
///     A generic type definition for an interface, e.g. typeof(ICollection&lt;&gt;) or typeof(IDictionary&lt;,&gt;).</param>
/// <param name="typeParameters">
///     Will receive an array containing the generic type parameters of the interface.</param>
/// <returns>
///     True if the current type is or implements the specified generic interface.</returns>
public static bool TryGetInterfaceGenericParameters(this Type type, Type @interface, out Type[] typeParameters)
{
    typeParameters = null;

    if (type.IsGenericType && type.GetGenericTypeDefinition() == @interface)
    {
        typeParameters = type.GetGenericArguments();
        return true;
    }

    var implements = type.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == @interface, null).FirstOrDefault();
    if (implements == null)
        return false;

    typeParameters = implements.GetGenericArguments();
    return true;
}

答案 4 :(得分:3)

我会测试IEnumerable,因为集合类型只能实现IEnumerable,所以不必实现IEnumerable<T>

这还取决于:集合类型是什么意思?您可以拥有一个集合,而无需实现任何这些接口。

答案 5 :(得分:3)

另外,请记住,因为您使用的是泛型,不要忘记其他基本技术,在这种情况下,就像重载一样。我怀疑你正在计划这样的事情:

void SomeFunc<T>(T t)
{
    if (IsCollectionCase(t))
       DoSomethingForCollections()
    else
       DoSOmethingElse();
}

这样可以更好地处理:

void SomeFunc(IEnumerable t)
{
       DoSomethingForCollections()
}
void SomeFunc<T>(T t)
{
       DoSomethingElse()
}

答案 6 :(得分:2)

虽然我不能确定原始海报的意图是什么,但是对于IEnumerable进行测试的铸造效果有几个回应。这很好,但是每个人都应该知道字符串实例通过了这个测试,这可能不是原作者想要的。我知道当我去寻找答案时我当然没有找到这篇文章:

string testString = "Test";
Console.WriteLine(testString as IEnumerable != null);  // returns true

我正在尝试编写一个使用反射来完成某些任务的自定义序列化程序。作为任务的一部分,我需要确定属性值是项的集合/数组/列表还是单个属性值。特别令人讨厌的是,几个Linq表达式实际上导致了一个可枚举的类型值,但GetType()。IsArray为这些返回false,并将它们转换为ICollection也返回null,但是将它们转换为IEnumerable会返回一个非null值。

所以......目前,我仍然在寻求一种适用于所有情况的解决方案。

答案 7 :(得分:1)

为了简单和代码共享,我通常使用这种扩展方法:

public static bool IsGenericList(this object obj)
{
    return IsGenericList(obj.GetType());
}

public static bool IsGenericList(this Type type)
{
    if (type == null)
    {
        throw new ArgumentNullException("type");
    }

    foreach (Type @interface in type.GetInterfaces())
    {
        if (@interface.IsGenericType)
        {
            if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>))
            {
                // if needed, you can also return the type used as generic argument
                return true;
            }
        }
    }

    return (type.GetInterface("IEnumerable") != null);
}

答案 8 :(得分:0)

我喜欢仿制药。在此方法中,T必须具有公共和无参数构造函数,这意味着您无法将IList<object>用于T。您必须使用List<object>

public static T IsEnumerable<T>() where T : new() {
    if (new T() is IEnumerable) {
}

答案 9 :(得分:0)

我在尝试将任何对象序列化为JSON格式时遇到了同样的问题。以下是我最终使用的内容:

Type typ = value.GetType();

// Check for array type
if(typeof(IEnumerable).IsAssignableFrom(typ) || typeof(IEnumerable<>).IsAssignableFrom(typ))
{ 
    List<object> list = ((IEnumerable)value).Cast<object>().ToList();
    //Serialize as an array with each item in the list...
}
else
{
    //Serialize as object or value type...
}

答案 10 :(得分:0)

如果您想进行检查并获得所有列表/集合/ IEnumerable的true,但要获取字符串类型的false,则

   private static bool IsIEnumerable(Type requestType)
   {
      var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(requestType);
      var notString = !typeof(string).IsAssignableFrom(requestType);
      return isIEnumerable && notString;
   }