3

外部物理測定デバイスを表すクラスがあります。簡易版は次のようになります。

public class Device {
    public string Tag { get; set; }
    public int Address { get; set; }
}

Tagデバイスを識別するためのユーザー定義の値です。Addressアダプターがデバイスと通信するために使用する値です。の 2 つのインスタンスがDevice同じAddressである場合、同じ外部測定デバイスが使用されます。

をオーバーライドして実装することにより、コードでその動作を模倣したいと思います(Containsおよびのようなメソッドを使用するため) :DistinctEqualsIEquatable<T>

public class Device : IEquatable<Device> {
    public string Tag { get; set; }
    public int Address { get; set; }

    public override bool Equals(object obj) {
        return Equals(obj as Device);
    }
    public bool Equals(Device other) {
        if (null == other) return false;
        if (ReferenceEquals(this, other)) return true;
        return Address.Equals(other.Address);
    }
}

ご覧のとおりTag、 の実装ではプロパティを無視していますEquals

だから、私の質問は:Tagの実装でプロパティを無視する必要がありEqualsますか? そうすることで、コードが理解しにくくなりますか? 私がやろうとしていることを行うためのより良い方法はありますか? Tagプロパティが必要なのは、多くの場合、ユーザーが を知らない、または が(App.config ファイルで処理されている) を持っているAddressかどうかさえ知らないためです。プロパティがあります)。DeviceAddressIDeviceAddress

アップデート:

回答ありがとうございます。

したがって、カスタムを使用する必要があると思いますIEqualityComparer。私の実際のコードがこのように見える場合、どうすればよいかについてのガイダンスはありますか?

public interface IDevice {
    string Tag { get; set; }
    double TakeMeasurement();
}
internal class Device : IDevice {
    public string Tag { get; set; }
    public int Address { get; set; }
    public double TakeMeasurement() {
        // Take a measurement at the device's address...
    }
}

でデバイスの種類を確認する必要がありますIEqualityComparerか?

public class DeviceEqualityComparer : IEqualityComparer<IDevice> {
    public bool Equals(IDevice x, IDevice y) {
        Contract.Requires(x != null);
        Contract.Requires(y != null);
        if ((x is Device) && (y is Device)) {
            return x.Address.Equals(y.Address);
        }
        else {
            return x.Equals(y);
        }
    }

    public int GetHashCode(IDevice obj) {
        Contract.Requires(obj != null);
        if (obj is Device) {
            return obj.Address.GetHashCode();
        }
        else {
            return obj.GetHashCode();
        }
    }
}
4

5 に答える 5

4

まず、オーバーライドするのを忘れたGetHashCode()ので、コードが壊れています。

IMO はEquals、2 つのオブジェクトがすべての目的で同等である場合にのみオーバーライドする必要があります。また、 が異なるオブジェクトは、Tag目的によっては異なるように見えます。

Equalsこれらのオブジェクトをオーバーライドすることはまったくありません。カスタムの比較機能を実装し、必要に応じてそれを使用したいと思いIEqualityComparer<T>ます。同等の概念を持つほとんどのメソッドは、IEqualityComparer<T>オプションのパラメーターとして を受け取ります。

nullパラメータを禁止するつもりはありませんが、それらを処理します。また、参照等価性のためのアーリーアウトも追加しました。

public class DeviceByAddressEqualityComparer : IEqualityComparer<IDevice> {
    public bool Equals(IDevice x, IDevice y) {
        if(x==y)
          return true;
        if(x==null||y==null)
          return false;
        return x.Address.Equals(y.Address);
    }

    public int GetHashCode(IDevice obj) {
        if(obj == null)
          return 0;
        else
          return obj.Address.GetHashCode();
    }
}

タイプを確認したい場合は、コンテキストに依存します。オーバーライドするときEqualsは通常 if でチェックしx.GetType()==y.GetType()ますが、ここではオブジェクトの一部を意図的に無視する特別な目的の比較子を使用しているため、おそらく型を ID の一部にしないでしょう。

于 2012-02-13T17:51:36.720 に答える
2

Yes, your current implementation is definitely confusing. The equality you've defined is clearly not the right notion of equality for devices.

So, rather than implementing IEquatable<Device> as you've done, I'd define an implementation of IEqualityComparer<Device>, maybe

class DeviceAddressEqualityComparer : IEqualityComparer<Device> {
    public bool Equals(Device x, Device y) {
        Contract.Requires(x != null);
        Contract.Requires(y != null);
        return x.Address.Equals(y.Address);
    }

    public int GetHashCode(Device obj) {
        Contract.Requires(obj != null);
        return obj.Address.GetHashCode();
    }
}

You can pass instances of IEqualityComparer<T> to Contains, Distinct and other LINQ methods that depend on equality (e.g., GroupBy).

于 2012-02-13T17:50:21.400 に答える
2

Equals の実装で Tag プロパティを無視する必要がありますか?

いいえ、これは悪い考えだと思います。

そうすることで、コードが理解しにくくなりますか?

当然のことですが、新しい開発者は、ハッシュ セットに異なるタグが設定された 2 つのデバイスがなぜ 1 つのデバイスになるのか理解できないでしょう。

私がやろうとしていることを行うためのより良い方法はありますか?

私が考えることができる少なくとも2つの方法があります:

  • カスタム コンパレータを提供する
  • というクラスを追加しDeviceWithTagDevice「タグなし」のままにします。

私は 2 番目のアプローチをお勧めします。これは、表示以外の目的でタグを無視するデバイスロケーターTagに貼り付けられた現実世界の「タグ」のように見えるためです。

于 2012-02-13T17:51:38.170 に答える
1

Tagまったく平等を実装する必要がありますか? あなたのようには聞こえないので、あなたのアプローチに問題はないと思います。

ユーザーが について知る必要がない場合はAddress、住所に基づいた基礎となる等価性について知る必要もないと主張するかもしれません... そうですか? そうでない場合でも、あなたのアプローチには何も問題はないと思います。

彼らが平等について知る必要がある場合は、設計を再考Addressし、何らかの方法で公開する必要があるかもしれません.

于 2012-02-13T17:50:59.403 に答える
0

実装するための新しいインターフェイスを作成することになりましたIDevice。また、新しいインターフェイスを使用すると、デバイスに基づいて等値比較子を簡単に作成できます。

public interface IPhysicallyEquatable<T>
{
    bool PhysicallyEquals(T other);
    int GetPhysicalHashCode();
}

public class PhysicalEqualityComparer<T> : IEqualityComparer<T>
    where T : IPhysicallyEquatable<T>
{
    public bool Equals(T x, T y)
    {
        if (null == x) throw new ArgumentNullException("x");
        if (null == y) throw new ArgumentNullException("y");
        return x.PhysicallyEquals(y);
    }
    public int GetHashCode(T obj)
    {
        if (null == obj) throw new ArgumentNullException("obj");
        return obj.GetPhysicalHashCode();
    }
}

public interface IDevice : IPhysicallyEquatable<IDevice>
{
    // ...
}
于 2012-02-13T19:43:27.830 に答える