ICollection / ICollection‹T› проблема неоднозначности

Просто хочу сделать простое расширение для синтаксического sygar:

public static bool IsNotEmpty(this ICollection obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}

public static bool IsNotEmpty<T>(this ICollection<T> obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}

Он отлично работает, когда я работаю с некоторыми коллекциями, но при работе с другими я получаю

Вызов неоднозначен между следующими методами или свойствами: «PowerOn.ExtensionsBasic.IsNotEmpty(System.Collections.IList)» и «PowerOn.ExtensionsBasic.IsNotEmpty(System.Collections.Generic.ICollection)».

Есть ли какое-нибудь каноническое решение этой проблемы?

Нет, я не хочу выполнять приведение типов перед вызовом этого метода;)


person Mose    schedule 09.10.2009    source источник
comment
Можете ли вы указать коллекцию, в которой есть эта проблема, чтобы мы могли проверить наши ответы?   -  person Marc Gravell    schedule 09.10.2009
comment
Вы уверены, что это декларации? Сообщение об ошибке, кажется, предполагает, что это IList, а не ICollection.   -  person Jon Skeet    schedule 09.10.2009
comment
Я также довольно часто сталкивался с этой проблемой, используя методы расширения для универсальных и неуниверсальных версий IEnumerable, ICollection и IList.   -  person Rex M    schedule 09.10.2009
comment
Нужен ли второй метод?   -  person Dave    schedule 09.10.2009
comment
Второй метод необходим, если вы имеете дело с типами, которые реализуют ICollection‹T›, но не ICollection.   -  person Jeff Sternal    schedule 09.10.2009
comment
Это особенно необходимо при работе с интерфейсами (когда вы не знаете конкретный тип экземпляра, с которым работаете): ICollection‹T› не реализует ICollection...   -  person Mose    schedule 12.10.2009
comment
@Marc Gravell: любая коллекция, которая явно реализует оба интерфейса, имеет эту проблему. Я создал новый и автоматически реализовал его в Visual Studio. Проблема воспроизвелась.   -  person Roman Boiko    schedule 24.11.2009
comment
@Jon Skeet: это потому, что List<T> реализует как ICollection, так и IList<T>, а IList<T> наследует ICollection<T>. Итак, List<T> реализует как ICollection, так и ICollection<T>.   -  person Roman Boiko    schedule 24.11.2009


Ответы (2)


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

((ICollection)myList).IsNotEmpty();

Or

((ICollection<int>)myIntList).IsNotEmpty();

И да, вы получите NullReferanceException, если obj == null, чтобы вы могли удалить нулевую проверку;) что означает, что ваш метод расширения просто сравнивает Count с 0, что вы можете сделать без метода расширения;)

person Arsen Mkrtchyan    schedule 09.10.2009
comment
Все универсальные версии реализуют свои необобщенные аналоги, поэтому любой универсальный класс будет иметь и то, и другое. - person Rex M; 09.10.2009
comment
Как я уже сказал в последнем предложении своего вопроса, я не хочу выполнять приведение типов перед вызовом. Это для SYNTACTIC SUGAR, если мне нужно добавить приведение, то это определенно бесполезно: p Мой вопрос остается: есть ли способ решить двусмысленность для работы со всеми коллекциями? - person Mose; 12.10.2009
comment
@ArsenMkrt: методы расширения можно вызывать для переменных с value == null, исключения не возникнет - person Roman Boiko; 24.11.2009
comment
@Rex M: вы имели в виду, что все универсальные классы из .NET Framework реализуют неуниверсальные интерфейсы? Есть ссылка на документацию? - person Roman Boiko; 24.11.2009

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

Вот весь код:

/// <summary>
/// Check the given array is empty or not
/// </summary>
public static bool IsNotEmpty(this Array obj)
{
    return ((obj != null)
        && (obj.Length > 0));
}
/// <summary>
/// Check the given ArrayList is empty or not
/// </summary>
public static bool IsNotEmpty(this ArrayList obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given BitArray is empty or not
/// </summary>
public static bool IsNotEmpty(this BitArray obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given CollectionBase is empty or not
/// </summary>
public static bool IsNotEmpty(this CollectionBase obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given DictionaryBase is empty or not
/// </summary>
public static bool IsNotEmpty(this DictionaryBase obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given Hashtable is empty or not
/// </summary>
public static bool IsNotEmpty(this Hashtable obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given Queue is empty or not
/// </summary>
public static bool IsNotEmpty(this Queue obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given ReadOnlyCollectionBase is empty or not
/// </summary>
public static bool IsNotEmpty(this ReadOnlyCollectionBase obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given SortedList is empty or not
/// </summary>
public static bool IsNotEmpty(this SortedList obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given Stack is empty or not
/// </summary>
public static bool IsNotEmpty(this Stack obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}
/// <summary>
/// Check the given generic is empty or not
/// </summary>
public static bool IsNotEmpty<T>(this ICollection<T> obj)
{
    return ((obj != null)
        && (obj.Count > 0));
}

Обратите внимание, что я не хотел, чтобы он работал на IEnumerable<T>, потому что Count() — это метод, который может инициировать запрос к базе данных, если вы работаете с Linq-to-Entity или Linq-to-SQL.

person Mose    schedule 12.10.2009
comment
После нескольких недель использования это ОПРЕДЕЛЕННО лучшее решение, мы массово внедрили его здесь. - person Mose; 26.11.2009