【问题标题】:Why can't I compare a KeyValuePair<TKey, TValue> with default为什么我不能将 KeyValuePair<TKey, TValue> 与默认值进行比较
【发布时间】:2011-09-16 19:39:34
【问题描述】:

在 .Net 2.5 中,我通常可以在一个值与其默认类型之间进行相等比较 (==)

if (myString == default(string))

但是,当我尝试对默认 KeyValuePair 和 KeyValuePair 运行相等比较时,出现以下异常

代码示例(来自预扩展方法,proto-lambda 静态 ListUtilities 类:))

public static TKey 
        FirstKeyOrDefault<TKey, TValue>(Dictionary<TKey, TValue> lookups, 
                   Predicate<KeyValuePair<TKey, TValue>> predicate)
{
    KeyValuePair<TKey, TValue> pair = FirstOrDefault(lookups, predicate);

    return pair == default(KeyValuePair<TKey, TValue>) ? 
                   default(TKey) : pair.Key;
}

例外:

运算符“==”不能应用于 类型的操作数 'System.Collections.Generic.KeyValuePair' 和 'System.Collections.Generic.KeyValuePair'

是不是因为 KeyValuePair 作为一个结构体不能为空?如果是这种情况,为什么默认实现了处理不可为空的类型?

编辑

为了记录,我选择@Chris Hannon 作为选择的答案,因为他给了我我想要的东西,最优雅的选择和简洁的解释,但是我鼓励阅读@Dasuraga 以获得非常全面的解释为什么会这样

【问题讨论】:

  • FirstOrDefault>(查找,谓词)?
  • @Jean-Bernard Pellerin,应该从传递的类型推断出来,否则编译器会抱怨。当您将其简化为 myKvp == default(KeyValuePair<...>) 时也会发生异常
  • 肯定默认是标量类型? KVP 具有默认值意味着是什么意思?我想你确实在你试图构建的任何上下文中都有一个答案。我怀疑对于非标量,默认值是调用 nil 构造函数所得到的。在这种情况下,我希望对象标识比较返回 false,因为每次调用构造函数时都会得到一个新对象。

标签: c# .net key-value


【解决方案1】:

发生这种情况是因为 KeyValuePair&lt;TKey, TValue&gt; 没有定义自定义 == 运算符,并且未包含在可以使用它的值类型的预定义列表中。

这是该操作员的MSDN documentation 的链接。

对于预定义的值类型,相等运算符 (==) 如果其操作数的值相等则返回 true,否则返回 false。

在这种情况下,您最好的平等检查方法是调用 default(KeyValuePair&lt;TKey,TValue&gt;).Equals(pair),因为这不是您可以控制的结构。

【讨论】:

  • 啊,确实有道理。谢谢!
  • 但不等于检查对象是否相等,即引用是否引用同一个对象?我不确定是否 default(int).Equals(0)...
  • @Dasuraga Console.WriteLine(default(int).Equals(0));输出“真”。我在考虑值类型,值在 Equals 上进行比较,否则在它们上使用 Equals 方法似乎毫无意义
  • @Dasuraga ==(以及所有其他运算符)是非虚拟的(object)kvp1 == (object)kvp2 将执行引用相等。强制转换对于更改编译时类型很重要,以确保调用object.==(object),这定义的运算符。
  • @Dasuraga 在结构体上调用 equals 时,实际上调用的是 ValueType 的实例方法,而不是 Object 的实例方法。 ValueType 的实例方法 Equals 使用反射来检查所有字段是否相等。参考:msdn.microsoft.com/en-us/library/ms173110.aspx
【解决方案2】:

失败的原因与以下相同:

var kvp = new KeyValuePair<string,string>("a","b");
var res = kvp == kvp;

线索自然就在错误信息中。 (与default无关)。

Operator '==' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,string>' and 'System.Collections.Generic.KeyValuePair<string,string>'

没有为KeyValuePair&lt;T,U&gt;定义运算符==

错误消息 FTW。

编码愉快。

【讨论】:

    【解决方案3】:

    为了在任何类或结构上使用“==”相等运算符,它需要覆盖运算符:http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx

    KeyValuePair 没有,因此您会收到编译错误。请注意,如果您尝试这样做,您会得到同样的错误:

    var k1 = new KeyValuePair<int,string>();
    var k2 = new KeyValuePair<int,string>();
    
    bool b = k1 == k2; //compile error
    

    编辑:正如 Eric Lippert 在 cmets 中纠正我的那样,类显然不需要重写相等运算符以使“==”有效。它会很好地编译并进行引用相等检查。我的错。

    【讨论】:

    • 任何类或结构——真的吗?如果你自己制作 C 类怎么办?您可以在不定义自定义 == 运算符的情况下比较两个 C 实例的相等性吗?
    【解决方案4】:

    默认设置为标量类型。

    问自己这个问题:KVP 有一个默认值意味着什么?

    对于非标量,默认值是调用 nil 构造函数得到的值。假设 KVP Equals 执行实例身份比较,我希望它返回 false,因为每次调用构造函数时都会得到一个新对象。

    【讨论】:

    • 在理想情况下,我希望 new KVP("Hello", "World") == new KVP("Hello", "World") 能够编译。但是,为了确保== 合约下降(a.Key == b.Key &amp;&amp; a.Value == b.Value),这将需要通用参数也响应==,这不能在编译时强制执行。请记住——包括自定义结构在内的每种类型都有一个默认值。
    【解决方案5】:

    (如果您不关心与此错误相关的泛型讨论,您可以直接跳到最后以获得“真实”答案)

    正如错误所说,KeyValuePairs 没有相等性测试(即没有内置的比较方法)。这样做的原因是为了避免必须对 KeyValuePairs 的类型进行限制(在很多情况下永远不会进行键值比较)。

    显然,如果您想比较这些 KeyValuePairs,我想您想要的是检查键和值是否相等。但这意味着一大堆东西,特别是 TKey 和 TValue 都是可比较的类型(即它们实现了 IComparable 接口)

    您可以编写自己的键值对之间的比较函数,例如:

    static bool KeyValueEqual<TKey , TValue>(KeyValuePair<TKey, TValue> fst, 
                                              KeyValuePair<TKey, TValue> snd) 
                                             where  TValue:IComparable
                                             where  TKey:IComparable
            {
                return (fst.Value.CompareTo(snd.Value)==0)
                         && (snd.Key.CompareTo(fst.Key)==0);
            }
    

    (请原谅糟糕的缩进)

    这里我们强制 TKey 和 TValue 都是可比较的(通过 CompareTo 成员函数)。

    当两个对象相等时,CompareTo 函数(为预定义类型定义)返回 0,à la strcmp。 a.ComparesTo(b)==0 表示 a 和 b 是“相同的”(在值上,不是同一个对象)。

    所以这个函数将采用两个 KVP (k,v) 和 (k',v') 并且当且仅当 k==k' 和 v==v' 时才会返回 true(在直观意义上)。


    但这有必要吗?似乎您遇到问题的测试是基于对 FirstOrDefault 返回的某种验证。

    但是你的函数被称为FirstOrDefault 是有原因的:

    返回第一个元素 满足条件的序列或 如果 没有这样的元素,则为 默认 值 找到了。

    (强调我的)

    此函数如果未找到某些内容,则返回默认值,这意味着如果您的谓词未经过验证,您将获得一个等于 (default(TKey),default(TValue) 的 KeyValuePair。

    因此,您的代码(打算)检查是否 pair.Key==default(TKey),只是为了无论如何都返回 default(TKey)。从一开始就返回 pair.Key 不是更有意义吗?

    【讨论】:

    • 非常好的答案,您的解决方法正是我要走的路,直到@Chris Hannon 指出使用 Equals()。请参阅我对您对该答案的评论的评论是对您对 Equals 不适用于值类型的担忧的回应。然而,我想应该是防御性的,并假设有一些我没有考虑的边缘条件......
    【解决方案6】:

    这是一个稍微不同的方向,但我假设你查询了一个字典来得到这个结果,然后你想检查它是否返回了一个有效的结果。

    我发现更好的方法是查询实际值而不是整个 KeyValuePair,如下所示:

    var valitem = MyDict.Values.FirstOrDefault(x=> x.Something == aVar);

    现在您可以检查 valitem 是否为空。同样,它不会直接回答您的问题,而是提供可能是您预期目标的替代方法。

    【讨论】:

      猜你喜欢
      • 2014-02-19
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多