почему List <T>.RemoveAll в универсальном классе <T>требует вариантной формы предиката? - PullRequest
3 голосов
/ 10 августа 2011

Если вы используете «RemoveAll» внутри универсального класса, который вы собираетесь использовать для хранения коллекции объектов любого типа, например:

public class SomeClass<T>
{
     internal List<T> InternalList;

     public SomeClass() { InternalList = new List<T>(); }

     public void RemoveAll(T theValue)
     {
       //  this will work
       InternalList.RemoveAll(x => x.Equals(theValue));

       // the usual form of Lambda Predicate 
       // for RemoveAll will not compile
       // error: Cannot apply operator '==' to operands of Type 'T' and 'T'
       // InternalList.RemoveAll(x => x == theValue);
     }
}

Ответы [ 4 ]

4 голосов
/ 10 августа 2011

Что ж, поскольку T может быть типом значения, вам нужно сделать что-то вроде этого:

  public class SomeClass<T> where T : class
  { 
    internal List<T> InternalList;

    public SomeClass() { InternalList = new List<T>(); }

    public void RemoveAll(T theValue)
    {
        //  this will work
        InternalList.RemoveAll(x => x == theValue);
    }
  }

Будьте осторожны, хотя просто проверяйте равенство ссылок - это то, что вам действительно нужно.

ОБНОВЛЕНИЕ: я забыл упомянуть об этом изначально, но это, конечно, будет означать, что вы не сможете использовать его для типов значений.В качестве альтернативы можно использовать что-то вроде этого для поддержки обоих типов:

public abstract class SomeCollection<T>
{
    internal List<T> InternalList;

    public SomeCollection() { InternalList = new List<T>(); }

    public abstract void RemoveAll(T theValue);
}

public class ReferenceCollection<T> : SomeCollection<T> where T : class
{
    public override void RemoveAll(T theValue)
    {
        InternalList.RemoveAll(x => x == theValue);
    }
}

public class ValueCollection<T> : SomeCollection<T> where T : struct
{
    public override void RemoveAll(T theValue)
    {
        InternalList.RemoveAll(x => x.Equals(theValue));
    }
}
2 голосов
/ 10 августа 2011

Если вы хотите сделать код максимально гибким, вы можете использовать EqualityComparer<T>.Default следующим образом:

public void RemoveAll(T theValue)
{
    //  this will work
    InternalList.RemoveAll(x => EqualityComparer<T>.Default.Equals(x, theValue));
}

Этот код будет работать для любого типа T (включая обнуляемые типы и типы значений), он избегает упаковки и также обрабатывает случаи, когда T реализует IEquatable<T> или переопределяет object.Equals. Из документации :

Свойство Default проверяет, реализует ли тип T System.IEquatable (T) интерфейс и, если так, возвращает EqualityComparer (T), который использует эту реализацию. В противном случае это возвращает EqualityComparer (T), который использует переопределения Object.Equals и Object.GetHashCode предоставлены T.

0 голосов
/ 10 августа 2011

operator== связан компилятором и требует, чтобы оба аргумента имели одинаковый тип времени компиляции.

Либо используйте явное сравнение ссылок, используя object.operator==:

   InternalList.RemoveAll(x => (object)x == (object)theValue);

, либоEquals (который может быть переопределен и учитывает тип среды выполнения)

   InternalList.RemoveAll(x => x.Equals(theValue));
0 голосов
/ 10 августа 2011

Должно работать для всех типов T Reference и ValueTypes.

Реализация по умолчанию для Equals в System.ValueType имеет следующее (из источника ссылки ms)

            // if there are no GC references in this object we can avoid reflection
            // and do a fast memcmp 
            if (CanCompareBits(this))
                return FastEqualsCheck(thisObj, obj); 

            FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            for (int i=0; i<thisFields.Length; i++) {
                thisResult = ((RtFieldInfo)thisFields[i]).InternalGetValue(thisObj,false);
                thatResult = ((RtFieldInfo)thisFields[i]).InternalGetValue(obj, false);

                if (thisResult == null) {
                    if (thatResult != null) 
                        return false; 
                }
                else 
                if (!thisResult.Equals(thatResult)) {
                    return false;
                }
            } 

В руководстве .net указано состояние :

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

Поэтому я считаю, что ответ заключается в том, что дизайнеры не хотят, чтобы люди полагались на неявный оператор для == для System.ValueTypes, и хотят, чтобы они реализовали лучшие версии, где это применимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...