如何比较实现相同接口的 2 个不同 类 是否相等?

How to compare 2 different classes implementing same interface for equality?

public interface IMyType{
    int Val1 {get; set;}
    int Val2 {get; set;}
}


public class ImplA : IMyType{
    public int Val1 {get; set;}
    public int Val2 {get; set;}
    public string Name {get; set;}
    
}

public class ImplB : IMyType{
    public int Val1 {get; set;}
    public int Val2 {get; set;}
    public int Age {get; set;}
    
}

List<IMyType> myTypes = new List<IMyType>();
myTypes.Add(new ImplA(){ Val1 = 100, Val2 = 200, Name ="John"});
myTypes.Add(new ImplA(){ Val1 = 500, Val2 = 600, Name ="Steve"});

IMyType t = new ImplB(){ Val1 = 100, Val2 = 200, Age =30});

bool exists = myTypes.Contains(t); //returns false because types are different

它比较实现类型的相等性和结果 'false' 是有道理的,但是有没有一种方法可以实现相等性并包含忽略实现类型而只比较接口属性的相等性? (在此示例中,它将 return 为真,因为 Val1 和 Val2 相等)

returns false because types are different

不,它 returns false 因为它是一个新实例。使用以下内容进行测试:

myTypes.Add(new ImplA(){ Val1 = 100, Val2 = 200, Name ="John"});
bool exists = myTypes.Contains(new ImplA(){ Val1 = 100, Val2 = 200, Name ="John"});

这仍然是 return false 所写的。

这是为什么?因为您没有在任何一种类型中覆盖默认的 object.Equals(object) 虚拟方法,并且默认实现调用 object.ReferenceEquals,这 return 是否两个对象具有相同的引用,即如果它们实际上是同一个实例。

据我了解,您想像这样覆盖 object.Equals

public class ImplA : IMyType
{
    public int Val1 {get; set;}
    public int Val2 {get; set;}
    public string Name {get; set;}

    public override bool Equals(object obj) =>
        obj is ImplA objA ? (Val1, Val2, Name) == (objA.Val1, objA.Val2, objA.Name)
        : obj is IMyType objI ? (Val1, Val2) == (objI.Val1, objI.Val2)
        : false;
}

是的。您可以使用反射。 有比这更好的方法,但你可以把它作为示例。

    public static bool Compare(IMyType a, IMyType b)
    {
        IList<PropertyInfo> properties = typeof(IMyType).GetProperties().ToList();
        bool risposta = true;
        foreach (var property in properties)
        {
            if (!property.GetValue(a).Equals(property.GetValue(b)))
            {
                risposta = false;
            }
        }
        return risposta;
    }

使用此方法检查对象是否在列表中。

public static bool CheckIfExists(List<IMyType> list, IMyType newObj) {
        foreach (IMyType m in list) {
            if(Compare(m, newObj))
            {
                return true;
            }
        }
        return false;
    }

并在列表中添加:

if (!CheckIfExists(myTypes, t)) {
            myTypes.Add(t);
        }

我完全支持 Blindy 的回答,纠正了它对对象比较方式(通过内存地址,没有任何 Equals/GetHashCode 的覆盖)的误解,这就是我建议的工作方式。

您在评论中说您不能覆盖 ImplX / IMyType 的 Equals,这令人惊讶,但如果这是必须遵守的约束,那么您可以更改检查列表是否包含匹配项的方式:

is there a way to do equality and contains such that it ignores the implementation types and only compares the interface properties for equality? (in this example it would return true since Val1 and Val2 are equal)

这取决于您所说的“包含”是什么意思。如果您的意思是确切且具体地使用 Contains(T),而不能为项目覆盖 Equals,那么 No 除非您想扩展列表并覆盖 Contains ..但是 LINQ 的 Any(=>) 可以告诉您是否list 包含一个项目,因为无论实际实现如何,它都是 IMyType,你可以这样做:

IMyType t = new ImplB(){ Val1 = 100, Val2 = 200, Age =30});
bool exists = myTypes.Any(i => i.Val1 == t.Val1 && i.Val2 == t.Val2);

如果您愿意,您也可以将其作为对 Contains 的覆盖添加到继承列表中..

如果您无法覆盖类型上的自定义 Equals,您始终可以为接口类型定义自定义相等比较器。

using System:
using System.Collections.Generic;

// implements IEqualityComparer<IMyType> 
// it's recommended to derive from EqualityComparer<T>
public class MyTypeComparer : EqualityComparer<IMyType>
{
   public override int GetHashCode(IMyType obj){
      return HashCode.Combine(obj.Val1, obj.Val2);
      // or if System.HashCode is unavailable, something like
      // return 37 ^ obj.Val1 ^ obj.Val2;
   } 
   public override bool Equals(IMyType a, IMyType b) {
      if (ReferenceEquals(a, b)) return true;
      if (a is null || b is null) return false;
      return a.Val1 == b.Val1 && a.Val2 == b.Val2;
   } 
} 

然后使用接受 IEqualityComparer<T> 而不是 List<T>.Contains

Enumerable.Contains 扩展方法
using System;
using System.Linq;
using System.Collections.Generic;

var myTypes = new List<IMyType>();
myTypes.Add(new ImplA(){ Val1 = 100, Val2 = 200, Name = "John"});
myTypes.Add(new ImplA(){ Val1 = 500, Val2 = 600, Name = "Steve"});

IMyType t = new ImplB(){ Val1 = 100, Val2 = 200, Age = 30 };
bool exists = myTypes.Contains(t, new MyTypeComparer());

Console.WriteLine(exists); // true

查看此 SharpLab 示例。