Почему я не могу сравнить KeyValuePair <TKey, TValue> со значением по умолчанию

В .Net 2.5 Обычно я могу получить сравнение равенства (==) между значением и его типом по умолчанию

if (myString == default(string))

Однако я получаю следующее исключение, когда пытаюсь выполнить сравнение равенства по умолчанию KeyValuePair и KeyValuePair

Пример кода (из метода предварительной расшифровки, прото-лямбатический статический класс 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 не имеет значения NULL? Если это так, почему, как, предположительно, по умолчанию было реализовано обращение к типам с нулевым значением?

EDIT

Для записи я выбрал @Chris Hannon в качестве выбранного ответа, поскольку он дал мне то, что я искал, самый элегантный вариант и краткое объяснение, однако я поощряю чтение @Dasuraga для очень подробного объяснения относительно почему это так.

Ответ 1

Это происходит потому, что KeyValuePair<TKey, TValue> не определяет пользовательский == оператор и не входит в предопределенный список типов значений, которые могут его использовать.

Вот ссылка на документацию MSDN для этого оператора.

Для предопределенных типов значений оператор равенства (==) возвращает true, если значения его операндов равны, в противном случае - false.

Ваш лучший выбор для проверки равенства в этом случае, потому что это не структура, над которой вы контролируете, заключается в вызове default(KeyValuePair<TKey,TValue>).Equals(pair).

Ответ 2

(Если вам неинтересно обсуждение обобщений, связанных с этой ошибкой, вы можете просто перейти к концу для своего "реального" ответа)

Как говорит ошибка, нет теста на равенство для KeyValuePairs (т.е. нет встроенного метода сравнения). Причина этого заключается в том, чтобы избежать необходимости устанавливать ограничения на типы KeyValuePairs (есть много случаев, когда ключевые значения сравнения никогда не будут выполняться).

Очевидно, что если вы хотите сравнить KeyValuePairs, я бы предположил, что вы хотите проверить, являются ли ключи и значения равными. Но это подразумевает целый беспорядок вещей, в частности, что TKey и TValue являются сопоставимыми типами (т.е. они реализуют интерфейс IComparable)

Вы можете написать свою собственную функцию сравнения между keyvaluepairs, например:

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 являются "одинаковыми" (по значению, а не одному и тому же объекту).

поэтому эта функция возьмет два KVPs (k, v) и (k ', v') и вернет true тогда и только тогда, когда k == k 'и v == v' (в интуитивном смысле).


Но нужно ли это? Кажется, ваш тест, в котором у вас возникли проблемы, основан на некоторой проверке при возврате FirstOrDefault.

Но есть причина, по которой ваша функция называется FirstOrDefault:

Возвращает первый элемент последовательность, удовлетворяющая условию или значение по умолчанию, если нет такого элемента найдено.

(акцент мой)

Эта функция возвращает значения по умолчанию, если что-то не найдено, что означает, что если ваш предикат не проверен, вы получите ключ KeyValuePair, равный (по умолчанию (TKey), по умолчанию (TValue).

Таким образом, ваш код (намеревается) проверяет, поддерживает ли пара .Key == default (TKey) только возврат по умолчанию (TKey). Разве это не имело бы больше смысла возвращать пару.Кей с самого начала?

Ответ 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

РЕДАКТИРОВАТЬ:. Как писал Эрик Липперт в комментариях, для классов явно не нужно переопределять оператор равенства для "==". Он будет компилироваться и выполнять контрольную проверку равенства. Моя ошибка.

Ответ 4

Он не работает по той же причине, что и:

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<T,U>.

Сообщения об ошибках FTW.

Счастливое кодирование.

Ответ 5

По умолчанию задаются скалярные типы.

Задайте себе этот вопрос: что значит для KVP иметь значение по умолчанию?

Для нескаляров по умолчанию используется то, что вы получаете от вызова конструктора nil. Предполагая, что KVP Equals выполняет сравнение идентичности экземпляра, я ожидаю, что он вернет false, поскольку вы получаете новый объект каждый раз при вызове конструктора.

Ответ 6

Это немного в другом направлении, но я предполагаю, что вы запросили словарь для получения этого результата, а затем хотите проверить, вернул ли он верный результат или нет.

Я нашел, что лучший способ сделать это - запросить фактическое значение вместо всего KeyValuePair, например:

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

Теперь вы можете проверить, является ли valitem нулевым или нет. Опять же, он напрямую не отвечает на ваш вопрос, но предлагает, какой может быть альтернативный подход к вашей намеченной цели.