Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I compare a KeyValuePair<TKey, TValue> with default

Tags:

c#

.net

key-value

In .Net 2.5 I can usually get an equality comparison (==) between a value and its type default

if (myString == default(string))

However I get the following exception when I try to run an equality comparison on a default KeyValuePair and a KeyValuePair

Code Sample (from a pre-extension method, proto-lambda static ListUtilities class :) )

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;
}

Exception:

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

Is it because, as a struct, the KeyValuePair is not nullable? If this is the case, why, as, presumably, default was implemented to handle not nullable types?

EDIT

For the record, I chose @Chris Hannon as selected answer, as he gave me what I was looking for, the most elegant option, and a succinct explanation, however I do encourage reading @Dasuraga for a very comprehensive explanation as to why this is the case

like image 772
johnc Avatar asked Jun 16 '11 23:06

johnc


3 Answers

This happens because KeyValuePair<TKey, TValue> does not define a custom == operator and is not included in the predefined list of value types that can use it.

Here is a link to the MSDN documentation for that operator.

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise.

Your best bet for an equality check in this case, because this is not a struct you have control over, is to call default(KeyValuePair<TKey,TValue>).Equals(pair) instead.

like image 56
Chris Hannon Avatar answered Nov 15 '22 21:11

Chris Hannon


(If you don't care about the generics discussion linked to this error, you can just jump to the end for your "real" answer)

As the error says, there is no equality testing for KeyValuePairs (i.e. there is no built-in comparison method). The reason for this is to avoid having to place constraints on the types of KeyValuePairs (there are many cases where key,value comparisons would never be made).

Obviously if you want to compare thes KeyValuePairs, I'd imagine what you'd want is to check if the keys and values are equal. But this implies a whole mess of things , notably that TKey and TValue are both comparable types (ie they implement the IComparable interface)

You could write your own comparison function between keyvaluepairs, for example:

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

(Excuse the awful indentation)

Here we impose that TKey and TValue are both comparable (via the CompareTo member function).

The CompareTo function (as defined for pre-defined types) returns 0 when two objects are equal , à la strcmp . a.ComparesTo(b)==0 means a and b are the "same"(in value, not the same object).

so this function would take two KVPs (k,v) and (k',v') and would return true if and only if k==k' and v==v' (in the intuitive sense).


But is this necessary? It seems your test where you're having problems is based on some sort of verification on the return of FirstOrDefault.

But there's a reason your function's called FirstOrDefault:

Returns the first element of the sequence that satisfies a condition or a default value if no such element is found.

(emphasis mine)

This function returns default values if something isn't found, meaning if your predicate isn't verified you'll get a KeyValuePair equal to (default(TKey),default(TValue).

Your code therefore (intends to) check whether pair.Key==default(TKey), only to return default(TKey) anyways. Wouldn't it just make more sense to return pair.Key from the outset?

like image 6
rtpg Avatar answered Nov 15 '22 21:11

rtpg


In order you to use the "==" equality operator on any class or struct, it needs to override the operator: http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx

KeyValuePair doesn't, and therefore you get the compile error. Note, you'll get the same error if you just try this:

var k1 = new KeyValuePair<int,string>();
var k2 = new KeyValuePair<int,string>();

bool b = k1 == k2; //compile error

EDIT: As Eric Lippert corrected me in the comments, classes obviously don't need to override the equality operator for "==" to be valid. It'll compile fine and do a reference equality check. My mistake.

like image 4
BFree Avatar answered Nov 15 '22 21:11

BFree