42

今日、私は自分が書いた興味深いバグに出くわしました。一般的なセッターで設定できるプロパティのセットがあります。これらのプロパティは、値型または参照型にすることができます。

public void SetValue( TEnum property, object value )
{
    if ( _properties[ property ] != value )
    {
        // Only come here when the new value is different.
    }
}

このメソッドの単体テストを作成するときに、値型の条件が常に真であることがわかりました。これがボクシング/アンボクシングによるものであると理解するのにそれほど時間はかかりませんでした。コードを次のように調整するのにも、それほど時間はかかりませんでした。

public void SetValue( TEnum property, object value )
{
    if ( !_properties[ property ].Equals( value ) )
    {
        // Only come here when the new value is different.
    }
}

問題は、私がこの解決策に完全に満足しているわけではないということです。値がボックスで囲まれていない限り、単純な参照比較を維持したいと思います。

私が考えている現在の解決策はEquals()、ボックス化された値のみを要求することです。ボックス化された値のチェックを行うのは少しやり過ぎのようです。もっと簡単な方法はありませんか?

4

5 に答える 5

26

値型を扱うときに別の動作が必要な場合は、明らかに何らかのテストを実行する必要があります。パラメータが。として入力されているため、すべての値型がボックス化**されるため、ボックス化された値型明示的にチェックする必要はありませんobject

このコードは、指定された基準を満たす必要があります。が(ボックス化された)値型の場合は、ポリモーフィックメソッドvalueを呼び出します。それ以外の場合は、を使用して参照の同等性をテストします。Equals==

public void SetValue(TEnum property, object value)
{
    bool equal = ((value != null) && value.GetType().IsValueType)
                     ? value.Equals(_properties[property])
                     : (value == _properties[property]);

    if (!equal)
    {
        // Only come here when the new value is different.
    }
}

Nullable<T>(**はい、それはボクシングとアンボクシングに関連する独自の特別なルールを持つ値型であることを私は知っていますが、それはここではほとんど無関係です。)

于 2011-06-02T00:10:09.370 に答える
12

Equals()は、一般的に推奨されるアプローチです。

.Equals()のデフォルトの実装は、参照型の単純な参照比較を行うため、ほとんどの場合、それが得られます。Equals()は他の動作を提供するためにオーバーライドされた可能性がありますが、誰かがクラスで.Equals()をオーバーライドした場合、それはその型の等式セマンティクスを変更したいためです。そうしないという説得力のある理由があります。==を使用してバイパスすると、他のすべてのクラスが同じであることに同意したときに、クラスが2つのものを異なるものと見なす場合、混乱を招く可能性があります。

于 2011-06-01T17:25:43.637 に答える
1

入力パラメータのタイプはobject。であるため、メソッドのコンテキスト内で常にボックス化された値を取得します。

私はあなたの唯一のチャンスはメソッドのシグネチャを変更し、異なるオーバーロードを書くことだと思います。

于 2011-06-01T17:23:09.433 に答える
1

これはどう:

if(object.ReferenceEquals(first, second)) { return; }
if(first.Equals(second)) { return; }

// they must differ, right?

アップデート

特定のケースでは、これが期待どおりに機能しないことに気付きました。

  • 値型の場合、ReferenceEqualsfalseを返すためEquals、期待どおりに動作するにフォールバックします。
  • trueを返す参照型についてReferenceEqualsは、期待どおりに「同じ」と見なされます。
  • ReferenceEqualsfalseを返し、falseを返す参照型の場合Equals、期待どおりに「異なる」と見なされます。
  • ReferenceEqualsfalseを返し、trueを返す参照型Equalsの場合、「異なる」が必要な場合でも、「同じ」と見なします。

ですから、教訓は「賢くならない」です

于 2011-06-01T17:24:52.890 に答える
0

私は考えます

値がボックスで囲まれていない限り、単純な参照比較を維持したいと思います。

やや同等です

値がボックスで囲まれている場合は、「単純な参照比較」ではありません。

つまり、最初に行う必要があるのは、値がボックス化されているかどうかを確認することです。

オブジェクトがボックス化された値型であるかどうかをチェックするメソッドが存在する場合、それが最も簡単な方法でない限り、リンクを提供した「オーバーキル」メソッドと少なくとも同じくらい複雑である必要があります。それでも、オブジェクトがボックス化された値型であるかどうかを判断するための「最も簡単な方法」が必要です。この「最も簡単な方法」が単にオブジェクトのEquals()メソッドを使用するよりも簡単である可能性は低いですが、念のためにこの質問をブックマークしました。

(私が論理的だったかどうかはわかりません)

于 2011-06-01T17:36:08.017 に答える