web-dev-qa-db-ja.com

KeyValuePair <TKey、TValue>をデフォルトと比較できないのはなぜですか

.Net 2.5では、通常、値とそのタイプのデフォルトとの間の等価比較(==)を取得できます

if (myString == default(string))

ただし、デフォルトのKeyValuePairとKeyValuePairで等値比較を実行しようとすると、次の例外が発生します。

コードサンプル(拡張前のメソッドから、proto-lambda static 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 <string、object>'および 'System.Collections.Generic.KeyValuePair <string、object>'のオペランドには適用できません

構造体として、KeyValuePairはnullにできないためですか?これが事実である場合、おそらく、デフォルトがnull許容型を処理するために実装されたのはなぜですか?

[〜#〜]編集[〜#〜]

記録のために、私が選択した回答として@Chris Hannonを選択しました。彼が私が探していたもの、最もエレガントなオプション、および簡潔な説明を与えられたからです。ケース

36
johnc

これは、_KeyValuePair<TKey, TValue>_がカスタム==演算子を定義せず、それを使用できる値タイプの事前定義リストに含まれていないために発生します。

これは、その演算子の MSDNドキュメント へのリンクです。

定義済みの値タイプの場合、等価演算子(==)は、そのオペランドの値が等しい場合はtrueを返し、それ以外の場合はfalseを返します。

この場合の等価チェックの最善策は、これはユーザーが制御できる構造体ではないため、代わりにdefault(KeyValuePair<TKey,TValue>).Equals(pair)を呼び出すことです。

58
Chris Hannon

(このエラーにリンクされたジェネリックのディスカッションを気にしない場合は、「実際の」答えの最後にジャンプできます)

エラーが示すように、KeyValuePairの等価性テストはありません(つまり、組み込みの比較メソッドはありません)。これは、KeyValuePairのタイプに制約を課す必要がないようにするためです(キーと値の比較が行われない場合が多くあります)。

明らかにKeyValuePairを比較したい場合は、キーと値が等しいかどうかを確認することをお勧めします。しかし、これは全体の混乱を意味します。特に、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関数(定義済みの型に対して定義されている)は、2つのオブジェクトが等しいとき、つまりstrcmpのとき0を返します。 a.ComparesTo(b)== 0は、aとbが(同じオブジェクトではなく)「同じ」であることを意味します。

したがって、この関数は2つのKVP(k、v)と(k '、v')を取り、k == k 'とv == v'の場合(直感的に)のみtrueを返します。


しかし、これは必要ですか?問題が発生しているテストは、FirstOrDefaultが返されたときの何らかの検証に基づいているようです。

しかし、関数が呼び出される理由があります FirstOrDefault

条件を満たすシーケンスの最初の要素、またはdefault値が見つからない場合はその値を返します。

(強調鉱山)

この関数は、何かが見つからない場合にデフォルト値を返します。つまり、述語が検証されない場合、(default(TKey )、default(TValue)。

したがって、コードは(意図している)pair.Key == default(TKey)かどうかをチェックし、default(TKey)を常に返すだけにします。最初からpair.Keyを返すほうが理にかなっていると思いませんか?

5
rtpg

「==」等号演算子をクラスまたは構造体で使用するには、演算子をオーバーライドする必要があります。 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がコメントで私を訂正したので、クラスが「==」を有効にするために等価演算子をオーバーライドする必要がないことは明らかです。それはうまくコンパイルし、参照の等価性チェックを行います。私の間違い。

4
BFree

以下と同じ理由で失敗します。

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。

ハッピーコーディング。

3
user166390

これは少し異なる方向に進みますが、この結果を取得するためにディクショナリをクエリし、それが有効な結果を返したかどうかを確認したいと思います。

これを行うより良い方法は、次のように、KeyValuePair全体ではなく実際の値をクエリすることであることがわかりました。

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

これで、valitemがnullかどうかを確認できます。繰り返しますが、それはあなたの質問に直接答えることはしませんが、あなたの意図した目標に対する代替のアプローチかもしれないものを提供します。

1
Brady Moritz

デフォルトはスカラー型で調整されます。

あなた自身にこの質問をしてください:それは何ですか平均 KVPがデフォルト値を持つために?

非スカラーの場合、デフォルトはnilコンストラクターの呼び出しから得られるものです。 KVP EqualsがインスタンスIDの比較を実行すると仮定すると、コンストラクターが呼び出されるたびに新しいオブジェクトを取得するため、falseを返すことが期待されます。

1
Peter Wone