IEnumerable<T, int>,Arity和泛型类型定义

4

我有一个类计数器,可以通过键值对进行计数。简化版如下:

public class Counter<T> {
    private Dictionary<T, int> counts;

    public void Increment(T key) {
        int current;
        bool exists = counts.TryGetValue(key, out current);
        if (exists) {
            counts[key]++;
        } else {
            counts[key] = 1;
        }
    }
}

它还有其他一些根据我的需求进行特殊处理的功能,但这就是本质。到目前为止,它运行得很好。

现在我想让它可以在Linq查询中使用(包括键和值)。为了实现这个目标,我认为需要实现:

IEnumerable<T, int>

所以我添加了:
public class Counter<T> : IEnumerable<KeyValuePair<T, int>> {
    // ...
    IEnumerator<KeyValuePair<T, int>> 
    IEnumerable<KeyValuePair<T, int>>.GetEnumerator()
    {
        return ((IEnumerable<KeyValuePair<T, int>>)counts).GetEnumerator();
    }
    System.Collections.IEnumerator 
    System.Collections.IEnumerable.GetEnumerator()
    {
        return counts.GetEnumerator();
    }

很不幸的是,这会导致编译器错误

提供的泛型参数数量与泛型类型定义的元数不相等。参数名称:实例化

问题

  1. 什么是元数?
  2. 我是否正在正确地使此类型可从Linq使用?
  3. 如何修复实现?

更新:笔误

我在简化代码以发布时发生了笔误。事实上,该代码正试图实现 IEnumerable<KeyValuePair<T, int>> 而不是 IEnumerable<T, int>


@AnthonyPegram: 只有一个通用类型参数。另一个类型int是固定的。这不允许吗?你能帮我提供一个参考吗? - Eric J.
3
Downvoter: 我希望知道你对这个问题有什么不满意之处。 - Eric J.
一个是T类型,一个是int类型这一事实并不重要。你也可以替换掉T类型,但你仍然提供了一个过多的参数。 - Anthony Pegram
安东尼是正确的。Arity只是意味着参数数量不正确。编译器期望您在实现IEnumerable<T>时提供恰好一个类型参数,因为IEnumerable<T>恰好有一个类型参数。 - Steven Wexler
@AnthonyPegram:我刚意识到我发布的代码(不是原始代码)中有一个错别字。事实上,我正在尝试实现IEnumerable<KeyValuePair<T, int>> - Eric J.
显示剩余4条评论
1个回答

10
  1. "Arity"的意思是“参数的数量”。这个词的根源包括“二进制”(需要两个参数的函数)、“一元”(需要一个参数的函数)和“三元”(需要三个参数的函数)。
  2. 不,不完全是这样的:LINQ根植于函数式编程,函数式编程会避免所有状态,并更喜欢没有副作用的函数。不幸的是,你的计数器保留了状态:那就是你修改的“counts”字典,它是一个副作用。
  3. 如果您想按键值计数,LINQ已经提供了足够的工具来实现。

以下是如何按键获取项目计数:

var counters = keyedData
    .GroupBy(item => item.MyKey)
    .ToDictionary(g => g.Key, g => g.Count());

谢谢,我不理解几个要点。#2:我可以在 Dictionary<K,V>上使用Linq,它与我的Counter<T>一样保持着状态。我的类型实际上只是一个方便的包装器。那么我的类型有什么不同呢?#3: Counter<T>每个具体键只有一个条目。Count()会如何处理它? - Eric J.
@EricJ。Dictionary<K,V>是LINQ语句的输出结果。字典是“制造”的,然后交给您。如果再次调用ToDictionary,将制造一个新实例。另一方面,在迭代输入的过程中修改了counter。如果选择两次迭代,则计数器将增加两次。如果选择在序列中间停止迭代,则部分元素将不被计算。 - Sergey Kalinichenko
@EricJ。在#3中,keyedData是您提供给Counter<T>的内容,而不是counter。它执行相同的操作:按组分组,并计算每个组中的项目数。基本上,这是您重新实现的counter,没有显式字典(当然,在LINQ的实现中存在,但您无法访问它,并且它是局部的ToDictionary方法,因此以其完整形式返回给您,即包含所有计数)。 - Sergey Kalinichenko
迭代不会改变存储在我的类型的后备字典中的内容。我一定是误解了什么...实际上,我正在累积大量数据。在运行时对每个事件进行分组将表示无法接受的性能损失。我不确定这是否是您建议的。 - Eric J.
@EricJ。也许我误解了你的意图。如果你提前累积了大量的数据,然后需要将这些数据提供给LINQ,那么你现在拥有的就是100%的好处:它编译并在ideone上运行([链接](http://ideone.com/DLilFJ)),执行它应该做的事情。 - Sergey Kalinichenko
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接