【问题标题】:Extension methods Dictionary<TKey,TValue>.RemoveAll? Is it possible?扩展方法 Dictionary<TKey,TValue>.RemoveAll?是否可以?
【发布时间】:2009-03-17 14:16:52
【问题描述】:

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

有什么建议吗?这是一个完全幼稚的实现吗?

【问题讨论】:

  • 您不想通过仅通过键而不是 KeyValuePair 匹配谓词来从字典中删除对吗?

标签: generics c#-3.0 extension-methods


【解决方案1】:

您的代码将不起作用,因为您正在按值传递 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 的顺序,以减少列表分配内存的大小。它现在只会为要删除的项目分配一个列表。

【讨论】:

  • 的缺点是每次都为key list分配足够的内存。但肯定很简单
  • @Rob 怎么会这样?适用于我使用过的示例数据
  • @Rob Stevenson-Leggett 确实如此。
  • 不会为我编译,将其更改为编辑后的版本,它可以工作。给了我:无法从使用中推断类型 args。
  • @Lucero,他没有循环它,他循环的是一个列表,它是它的投影。 @Rob Stevenson-Leggett,我很惊讶你接受了这个答案,因为它改变了你想要的函数的签名(旨在复制 List.RemoveAll)。您可以将 Func 条件替换为 Predicate 匹配,然后 .Where(x => match(x))
【解决方案2】:
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 的会更干净、更简单。

【讨论】:

  • 不需要在条件上调用condition.Invoke(...) 方法,因为它已经是一个委托了。您可以直接调用条件,例如条件(项目)。
  • @base2 我只是在复制原始用户的风格。我同意没有调用会更好,我会改变它
【解决方案3】:

该方法不起作用,因为“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 的实现。

【讨论】:

    【解决方案4】:

    但如果你愿意,你可以返回一个新的和不同的字典。你的签名会变成这样:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-15
      • 2010-10-09
      相关资源
      最近更新 更多