Определить, является ли объект производным от типа коллекции - PullRequest
20 голосов
/ 15 апреля 2009

Я хочу определить, является ли параметр типа метода универсального типа объекта ("T") типом коллекции. Обычно я посылаю T через Generic.List, но это может быть любой тип коллекции, так как он используется в вспомогательной функции.

Будет ли лучше проверить, реализует ли он IEnumerable ?

Если это так, как будет выглядеть код?

Обновление 14:17 GMT + 10 Возможно расширение на решение здесь (однако работает только для List , а не IEnumerable , когда это должно произойти, если List получит? )

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

Ответы [ 10 ]

31 голосов
/ 15 апреля 2009

Это будет самая простая проверка ..

if(Obj is ICollection)
{
    //Derived from ICollection
}
else
{
    //Not Derived from ICollection
}
15 голосов
/ 15 апреля 2009

Вы можете использовать Type.GetInterface () с искаженным именем.

private bool IsTAnEnumerable<T>(T x)
{
    return null != typeof(T).GetInterface("IEnumerable`1");
}
8 голосов
/ 15 апреля 2009

Чтобы получить фактический тип 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
7 голосов
/ 05 июня 2014

Лично я склонен использовать метод, который я написал сам, под названием 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;
}
3 голосов
/ 15 апреля 2009

Кроме того, помните только, что вы используете дженерики, не забывайте другие базовые методы, в данном случае, например, перегрузку. Я подозреваю, что вы планируете что-то вроде этого:

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

Это будет гораздо лучше обрабатываться как:

void SomeFunc(IEnumerable t)
{
       DoSomethingForCollections()
}
void SomeFunc<T>(T t)
{
       DoSomethingElse()
}
3 голосов
/ 15 апреля 2009

Я бы протестировал IEnumerable вместо этого, поскольку тип коллекции может реализовывать только IEnumerable, он не должен реализовывать IEnumerable<T>.

Это также зависит: что вы имеете в виду под типом коллекции ? Вы можете получить коллекцию, не реализовав ни один из этих интерфейсов.

2 голосов
/ 18 ноября 2013

Хотя я не могу быть уверен, каково было намерение первоначального автора, было несколько ответов на эффект приведения к IEnumerable для тестирования. Это хорошо, но все должны знать, что строковые экземпляры проходят этот тест, что, возможно, не то, что предполагал оригинальный автор. Я знаю, что точно не знал, когда пошел искать ответ и нашел этот пост:

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

Я пытаюсь написать собственный сериализатор, который использует отражение для выполнения определенных задач. Как часть задачи, мне нужно определить, является ли значение свойства коллекцией / массивом / списком элементов или единственным значением свойства. Что особенно раздражает, так это то, что несколько выражений Linq фактически приводят к перечисляемому значению типа, но GetType (). IsArray возвращает false для них, а приведение их к ICollection также возвращает null, но приведение их к IEnumerable возвращает ненулевое значение.

Итак ... на данный момент я все еще ищу решение, которое работает для всех случаев.

1 голос
/ 04 февраля 2018

Для простоты и совместного использования кода я обычно использую этот метод расширения:

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);
}
0 голосов
/ 08 июля 2016

Я столкнулся с той же проблемой при попытке сериализации любого объекта в формат 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...
}
0 голосов
/ 10 ноября 2015

Я люблю дженерики. В этом методе T должен иметь открытый конструктор без параметров, что означает, что вы не можете использовать IList<object> для T. Вы должны использовать List<object>

public static T IsEnumerable<T>() where T : new() {
    if (new T() is IEnumerable) {
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...