C# 压倒一切的IEquatable<;T>;当T是接口且派生类型之间的哈希代码不同时

C# 压倒一切的IEquatable<;T>;当T是接口且派生类型之间的哈希代码不同时,c#,.net,interface,gethashcode,iequatable,C#,.net,Interface,Gethashcode,Iequatable,我有A和B两个类都实现了接口I public interface I { int SomeInt { get; } bool SomeBool { get; } float SomeFloat { get; } } public class A : I { public int SomeInt { get; } public bool SomeBool { get; } public float SomeFloat { get; }

我有
A
B
两个类都实现了接口
I

public interface I
{
    int SomeInt { get; }
    bool SomeBool { get; }
    float SomeFloat { get; }
}


public class A : I
{
    public int SomeInt { get; }
    public bool SomeBool { get; }
    public float SomeFloat { get; }

    private readonly string _someARelatedStuff;
    // Rest of class...
}

public class B : I
{
    public int SomeInt { get; }
    public bool SomeBool { get; }
    public float SomeFloat { get; }

    private string readonly _someBRelatedStuff;
    private double readonly _someOtherBRelatedStuff;
    // Rest of class...
}
有时我想基于
I
属性的相等性(
SomeInt
SomeBool
SomeFloat
)来测试
A
B
之间的相等性(通常在比较
A
列表和
B
列表时),因此,我在这两个方面都实现了
IEquatable
,并根据它们共享的
I
属性值对它们进行了比较

问题是我已经在A和B上实现了
GetHashCode()
,它会产生不同的散列,因为我考虑了其他成员

B
不依赖于
A
,所以我使用接口
I
来比较它们,它有一个带有getter的属性列表

我读到:

如果您正在实现一个类,您应该始终确保两个相等的对象具有相同的哈希代码

那么,这是否意味着每当类
a
想要成为实现接口
I
,并且我想要能够比较实现
I
的实例时,我必须确保对
I
的所有实例都以相同的方式计算hashcode,并且只使用
I
属性

当T是一个接口时,我确实觉得我不打算实现
IEquatable
,但我的备选方案是:

  • 对基类使用常规继承-如果可能的话,我宁愿避免继承,如果B需要从某个框架
    C
    类派生,由于单一继承,这个解决方案将不起作用
  • 使用
    A
    B
    上的方法执行
    A
    B
    之间的相等性检查-将创建代码重复
  • I
    中定义的
    I
    实例之间使用相等检查方法-听起来是最好的选择

  • 是否有我缺少的选项?

    考虑使用a
    IEqualityComparer
    类来比较常用值

    为了便于阅读,我已将界面重命名为
    ICommon

    公共接口ICommon
    {
    int SomeInt{get;}
    bool SomeBool{get;}
    float SomeFloat{get;}
    }
    公共类CommonComparer:IEqualityComparer
    {
    公共布尔等于(ICommon x,ICommon y)
    {
    返回x.SomeInt.Equals(y.SomeInt)
    &&x.SomeBool等于(y.SomeBool)
    &&x.SomeFloat等于(y.SomeFloat);
    }
    公共int GetHashCode(ICommon obj)
    {
    未经检查
    {
    int hc=-1817952719;
    hc=(-1521134295)*hc+obj.SomeInt.GetHashCode();
    hc=(-1521134295)*hc+obj.SomeBool.GetHashCode();
    hc=(-1521134295)*hc+obj.SomeFloat.GetHashCode();
    返回hc;
    }
    }
    }
    
    该程序可以区分两个列表中相同的项目

    class Program
    {
        static void Main(string[] args)
        {
            var listA = new List<A>
            {
                new A(1001, true, 1.001f, "A1"),
                new A(1002, true, 1.002f, "A2"),
                new A(1003, false, 1.003f, "A1"),
                new A(1004, false, 1.004f, "A4")
            };
    
            var listB = new List<B>
            {
                new B(1001, true, 1.001f, "B1", 2.5),
                new B(1002, false, 1.002f, "B2", 2.8),
                new B(1003, true, 1.003f, "B3", 2.9),
                new B(1004, false, 1.004f, "B4", 2.9)
            };
    
            var common = Enumerable.Intersect(listA, listB, new CommonComparer()).OfType<ICommon>();
    
    
            Console.WriteLine($"{"SomeInt",-8} {"Bool",-6} {"SomeFloat",-10}");
            foreach (var item in common)
            {
                Console.WriteLine($"{item.SomeInt,-8} {item.SomeBool,-6} {item.SomeFloat,-10}");
            }
            //SomeInt  Bool   SomeFloat
            //1001     True   1.001
            //1004     False  1.004
    
        }
    }
    
    类程序
    {
    静态void Main(字符串[]参数)
    {
    var listA=新列表
    {
    新A(1001,正确,1.001f,“A1”),
    新A(1002,真,1.002f,“A2”),
    新A(1003,假,1.003f,“A1”),
    新A(1004,假,1.004f,“A4”)
    };
    var listB=新列表
    {
    新B(1001,正确,1.001f,“B1”,2.5),
    新B(1002,假,1.002f,“B2”,2.8),
    新B(1003,正确,1.003f,“B3”,2.9),
    新B(1004,假,1.004f,“B4”,2.9)
    };
    var common=Enumerable.Intersect(listA、listB、new CommonComparer())。of type();
    WriteLine($“{”SomeInt“,-8}{”Bool“,-6}{”SomeFloat“,-10}”);
    foreach(通用var项目)
    {
    WriteLine($“{item.SomeInt,-8}{item.SomeBool,-6}{item.SomeFloat,-10}”);
    }
    //有点像有点像浮球
    //1001真1.001
    //1004假1.004
    }
    }
    
    以及其余的代码定义

    public class A : ICommon, IEquatable<A>
    {
        static readonly CommonComparer comparer = new CommonComparer();
    
        public int SomeInt { get; }
        public bool SomeBool { get; }
        public float SomeFloat { get; }
    
        private readonly string _someARelatedStuff;
        // Rest of class...
        public A(ICommon other, string someARelatedStuff)
            : this(other.SomeInt, other.SomeBool, other.SomeFloat, someARelatedStuff)
        { }
        public A(int someInt, bool someBool, float someFloat, string someARelatedStuff)
        {
            this.SomeInt = someInt;
            this.SomeBool = someBool;
            this.SomeFloat = someFloat;
            this._someARelatedStuff = someARelatedStuff;
        }
    
        public override string ToString() => _someARelatedStuff;
    
        #region IEquatable Members
        public override bool Equals(object obj)
        {
            if (obj is A other)
            {
                return Equals(other);
            }
            return false;
        }
    
    
        public virtual bool Equals(A other)
        {
            return comparer.Equals(this, other)
                && _someARelatedStuff.Equals(other._someARelatedStuff);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int hc = comparer.GetHashCode(this);
                hc = (-1521134295)*hc + _someARelatedStuff.GetHashCode();
                return hc;
            }
        }
    
        #endregion
    
    }
    
    public class B : ICommon, IEquatable<B>
    {
        static readonly CommonComparer comparer = new CommonComparer();
    
        public int SomeInt { get; }
        public bool SomeBool { get; }
        public float SomeFloat { get; }
    
        readonly string _someBRelatedStuff;
        readonly double _someOtherBRelatedStuff;
        // Rest of class...
    
        public B(ICommon other, string someBRelatedStuff, double someOtherBRelatedStuff)
            : this(other.SomeInt, other.SomeBool, other.SomeFloat, someBRelatedStuff, someOtherBRelatedStuff)
        { }
        public B(int someInt, bool someBool, float someFloat, string someBRelatedStuff, double someOtherBRelatedStuff)
        {
            this.SomeInt = someInt;
            this.SomeBool = someBool;
            this.SomeFloat = someFloat;
            this._someBRelatedStuff = someBRelatedStuff;
            this._someOtherBRelatedStuff = someOtherBRelatedStuff;
        }
    
        public override string ToString() => $"{_someBRelatedStuff}, {_someOtherBRelatedStuff.ToString("g4")}";
    
        #region IEquatable Members
    
        public override bool Equals(object obj)
        {
            if (obj is B other)
            {
                return Equals(other);
            }
            return false;
        }
    
        public virtual bool Equals(B other)
        {
            return comparer.Equals(this, other)
                && _someBRelatedStuff.Equals(other._someBRelatedStuff)
                && _someOtherBRelatedStuff.Equals(other._someOtherBRelatedStuff);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int hc = comparer.GetHashCode(this);
                hc = (-1521134295)*hc + _someBRelatedStuff.GetHashCode();
                hc = (-1521134295)*hc + _someOtherBRelatedStuff.GetHashCode();
                return hc;
            }
        }
    
        #endregion
    }
    
    公共A类:ICommon,IEquatable
    {
    静态只读CommonComparer comparer=新CommonComparer();
    公共int SomeInt{get;}
    公共bool SomeBool{get;}
    公共浮点SomeFloat{get;}
    私有只读字符串_somearestuff;
    //其他同学。。。
    公共A(ICommon-other,字符串someAreatedStuff)
    :此(other.SomeInt,other.SomeBool,other.SomeFloat,someARelatedStuff)
    { }
    公共A(int-someInt,bool-someBool,float-someFloat,string-someARelatedStuff)
    {
    this.SomeInt=SomeInt;
    this.SomeBool=SomeBool;
    this.SomeFloat=SomeFloat;
    这._somearestuff=somearestuff;
    }
    公共重写字符串ToString()=>\u someARelatedStuff;
    #区域可容纳成员
    公共覆盖布尔等于(对象对象对象)
    {
    如果(obj是另一个)
    {
    回报等于(其他);
    }
    返回false;
    }
    公共虚拟布尔等于(其他)
    {
    返回比较器.Equals(此,其他)
    &&_somearestuff.等于(其他._somearestuff);
    }
    公共覆盖int GetHashCode()
    {
    未经检查
    {
    int hc=comparer.GetHashCode(this);
    hc=(-1521134295)*hc+_somearestuff.GetHashCode();
    返回hc;
    }
    }
    #端区
    }
    公共B类:ICommon,IEquatable
    {
    静态只读CommonComparer comparer=新CommonComparer();
    公共int SomeInt{get;}
    公共bool SomeBool{get;}
    公共浮点SomeFloat{get;}
    只读字符串_someBRelatedStuff;
    只读双_someotherbreatedstuff;
    //其他同学。。。
    公共B(ICommon other,字符串someBRelatedStuff,double someOtherBRelatedStuff)
    :此(other.SomeInt,other.SomeBool,other.SomeFloat,someBRelatedStuff,someOtherBRelatedStuff)
    { }
    公共B(int-someInt,bool-someBool,float-someFloat,
    
    public class A : ICommon, IEquatable<A>
    {
        static readonly CommonComparer comparer = new CommonComparer();
    
        public int SomeInt { get; }
        public bool SomeBool { get; }
        public float SomeFloat { get; }
    
        private readonly string _someARelatedStuff;
        // Rest of class...
        public A(ICommon other, string someARelatedStuff)
            : this(other.SomeInt, other.SomeBool, other.SomeFloat, someARelatedStuff)
        { }
        public A(int someInt, bool someBool, float someFloat, string someARelatedStuff)
        {
            this.SomeInt = someInt;
            this.SomeBool = someBool;
            this.SomeFloat = someFloat;
            this._someARelatedStuff = someARelatedStuff;
        }
    
        public override string ToString() => _someARelatedStuff;
    
        #region IEquatable Members
        public override bool Equals(object obj)
        {
            if (obj is A other)
            {
                return Equals(other);
            }
            return false;
        }
    
    
        public virtual bool Equals(A other)
        {
            return comparer.Equals(this, other)
                && _someARelatedStuff.Equals(other._someARelatedStuff);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int hc = comparer.GetHashCode(this);
                hc = (-1521134295)*hc + _someARelatedStuff.GetHashCode();
                return hc;
            }
        }
    
        #endregion
    
    }
    
    public class B : ICommon, IEquatable<B>
    {
        static readonly CommonComparer comparer = new CommonComparer();
    
        public int SomeInt { get; }
        public bool SomeBool { get; }
        public float SomeFloat { get; }
    
        readonly string _someBRelatedStuff;
        readonly double _someOtherBRelatedStuff;
        // Rest of class...
    
        public B(ICommon other, string someBRelatedStuff, double someOtherBRelatedStuff)
            : this(other.SomeInt, other.SomeBool, other.SomeFloat, someBRelatedStuff, someOtherBRelatedStuff)
        { }
        public B(int someInt, bool someBool, float someFloat, string someBRelatedStuff, double someOtherBRelatedStuff)
        {
            this.SomeInt = someInt;
            this.SomeBool = someBool;
            this.SomeFloat = someFloat;
            this._someBRelatedStuff = someBRelatedStuff;
            this._someOtherBRelatedStuff = someOtherBRelatedStuff;
        }
    
        public override string ToString() => $"{_someBRelatedStuff}, {_someOtherBRelatedStuff.ToString("g4")}";
    
        #region IEquatable Members
    
        public override bool Equals(object obj)
        {
            if (obj is B other)
            {
                return Equals(other);
            }
            return false;
        }
    
        public virtual bool Equals(B other)
        {
            return comparer.Equals(this, other)
                && _someBRelatedStuff.Equals(other._someBRelatedStuff)
                && _someOtherBRelatedStuff.Equals(other._someOtherBRelatedStuff);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                int hc = comparer.GetHashCode(this);
                hc = (-1521134295)*hc + _someBRelatedStuff.GetHashCode();
                hc = (-1521134295)*hc + _someOtherBRelatedStuff.GetHashCode();
                return hc;
            }
        }
    
        #endregion
    }