1

今日、いくつかのコードをリファクタリングしているときに、非常に奇妙な動作に遭遇しました。

次のようなコードがいくつかありました。

    private AType Blah
    {
        get
        {
            return (from something in AList
                    where _x == null || something.x == _x
                    where _y == null || something.y == _y
                    where _z == null || something.z.IsSameAs(_z)
                    select something).Single();
        }
    }

型名と変数名は質問にとって重要ではないため、匿名化しました。

_x と something.x の型は文字列で、_y と something.y は参照型でした。同様に _z と something.z は、値比較を伴う参照型でした。

私はこのようなことができると思いました:

    public AType Blah
    {
        get { return AList.Single(something => DetailsMatch(something.x, something.y, something.z)); }
    }

    private bool DetailsMatch(string x, AnotherType y, AFurtherType z)
    {
        return NullOrCheck(_x, x) &&
               NullOrCheck(_y, y) &&
               NullOrCheck(_z, z.IsSameAs);
    }

    private bool NullOrCheck<T>(T value, T expected) where T : class
    {
        return NullOrCheck(value, v => v == expected);
    }

    private static bool NullOrCheck<T>(T value, Func<T,bool> check) where T : class
    {
        return value == null || check(value);
    }

これはすべて理にかなっているように見えましたが、驚いたことに、いくつかのテストが失敗し始めました。== 演算子を使用すると、同一の文字列 (例では "1A04" と "1A04") が等しいと見なされなくなったことが判明しました。

以下を見たCan't operator == be applied to generic types in C#? 通常の方法ではなく、文字列が参照の等価性で比較されている可能性があります。

c#でこれを行う安全な方法はありますか、またはジェネリックメソッドで == を使用することは上記の理由で危険と見なされるべきですか?

これが問題であることを確認するために、私の修正は、問題のあるメソッドを文字列ケースにインライン化することで構成され、結果として次のようになりました。

    private bool DetailsMatch(string x, AnotherType y, AFurtherType z)
    {
        return (_x == null || _x == x) &&
               NullOrCheck(_y, y) &&
               NullOrCheck(_z, z.IsSameAs);
    }

ちょっとプレスト-すべてが機能し、テストは再びパスします

4

1 に答える 1

4

使用できますObject.Equals

return NullOrCheck(value, v => object.Equals(v, expected));

stringクラスは静的演算子をオーバーロード==して、2 つの文字列引数が等しいかどうかを比較します。

string first = "abc";
string second = "abc";

bool eq = first == second;

との静的型は両方ともであるため、への呼び出し==はオーバーロードされた文字列を使用します。==firstsecondstring

ただし、

object first = "abc";
object second = "abc";
bool eq = first == second;

andの静的型はオブジェクトであるため==、使用される演算子は によって定義されたものになります。この場合、文字列のインターンにより、実際には同じ文字列への参照が含まれることに注意してください。ただし、これは一般的には当てはまりません。objectfirstsecondfirstsecond

ジェネリック メソッドでは、 は に定義されたより具体的なバージョンではなく、 に定義され==た静的に解決されます。は s の単純な参照等価性チェックであるため、これは異なる動作をします。==objectstring==object

このEqualsメソッドは仮想であり、カスタム型の等値チェックを特殊化するためにオーバーライドできます。したがって、

object first = "abc";
object second = "abc";
bool eq = first.Equals(second);

メソッドが呼び出され、参照が同じであるだけでなく、文字列のstring.Equals値が同じであることを確認します。

静的object.Equalsメソッドは仮想Equalsインスタンス メソッドを使用するため、同じ文字列インスタンスを指すだけでなく、文字列が同じ値を持つことも確認します。staticobject.Equalsは引数の null もチェックするため、objA.Equals(objB)直接呼び出すよりも安全です。

于 2013-05-14T12:17:18.377 に答える