扩展方法Dictionary <tkey,tvalue> .RemoveAll?有可能吗?</tkey,tvalue>

时间:2009-03-17 14:16:52

标签: generics c#-3.0 extension-methods

我一直在尝试编写一个扩展方法来模仿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;
}

任何指针?这是一个完全天真的实现吗?

4 个答案:

答案 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"));