为什么List泛型类中的RemoveAll <需要谓词的变体形式
本文关键字:谓词 RemoveAll 泛型类 为什么 List | 更新日期: 2023-09-27 18:05:08
如果你在一个泛型类中使用'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);
}
}
既然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);
}
}
要小心,检查引用是否相等才是你真正想要的。
UPDATE:我最初忘记提到这一点,但这当然意味着你将无法将其用于值类型。另一种选择是使用类似这样的东西来同时支持:
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));
}
}
如果你想让代码尽可能灵活,你可以这样使用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),它使用的是的重写对象。等号和对象。
提供的GetHashCode
它必须适用于所有类型的T Reference和valuetype。
系统中Equals的默认实现。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 ==的隐式操作符。
operator==
受编译器约束,要求两个参数具有相同的编译时类型。
通过使用object.operator==
:
InternalList.RemoveAll(x => (object)x == (object)theValue);
或Equals
(可能被覆盖并考虑运行时类型)
InternalList.RemoveAll(x => x.Equals(theValue));