我一直在尝试编写一个扩展方法来模仿List.RemoveAll(Predicate)。
到目前为止,我已经得到了这个:
public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict,
Predicate<KeyValuePair<TKey,TValue>> condition)
{
Dictionary<TKey,TValue> temp = new Dictionary<TKey,TValue>();
foreach (var item in dict)
{
if (!condition.Invoke(item))
temp.Add(item.Key, item.Value);
}
dict = temp;
}
任何指针?这是一个完全天真的实现吗?
答案 0 :(得分:16)
您的代码无效,因为您按值传递Dictionary类。这意味着调用函数将无法看到最终赋值(dict = temp)。在C#中通过ref或out传递扩展方法目标是不合法的(在VB中,做ByRef是合法的)。
相反,您需要修改字典内联。请尝试以下
public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict,
Func<KeyValuePair<TKey,TValue>,bool> condition)
{
foreach ( var cur in dict.Where(condition).ToList() ) {
dict.Remove(cur.Key);
}
}
编辑
交换Where和ToList的顺序以减少列表的已分配内存的大小。现在它只会为要删除的项目分配一个列表。
答案 1 :(得分:4)
public static void RemoveAll<TKey,TValue>(
this Dictionary<TKey,TValue> dict,
Predicate<KeyValuePair<TKey,TValue>> condition)
{
var toRemove = new List<TKey>();
foreach (var item in dict)
{
if (!condition(item))
toRemove.Add(item);
}
foreach (var key in toRemove)
{
dict.Remove(key);
}
}
如果要删除的键数相对于字典大小较小,则速度会更快(如果删除的数字可能为零,则可以通过懒惰地创建toRemove列表来更快地进行此操作。
这归结为与Jared更新的答案相同,但如果您愿意,可以推迟删除列表的创建。如果这不是一个问题(你没有理由在整个过程中突破点)那么Jared的更清洁,更简单。
答案 2 :(得分:1)
该方法不起作用,因为“dict”参数不是通过引用传递的,实际上不能是因为不支持ref作为扩展方法的第一个参数。
public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict,
Predicate<KeyValuePair<TKey,TValue>> condition)
{
var temp = new List<TKey>();
foreach (var item in dict)
{
if (!condition(item))
temp.Add(item.Key);
}
foreach (var itemKey in temp)
dict.Remove(itemKey)
}
我也希望看到RemoveAllByKey和RemoveAllByValue实现。
答案 3 :(得分:0)
但如果你想,你可以返回一个新的不同的词典。您的签名将更改为:
public static Dictionary<TKey, TValue> RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict,
Predicate<KeyValuePair<TKey,TValue>> condition)
调用代码会说:
var newDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something"));
而且,如果你想修改oldDict,你可以这样称呼它:
oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something"));