简体   繁体   English

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

[英]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

It makes sense that it compares the implementation types for equality and results in 'false' but 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) (在这个例子中它会返回真,因为 Val1 和 Val2 是相等的)

If you can't override a custom Equals on your types, you can always define a custom Equality Comparer for the interface type.如果您无法在您的类型上覆盖自定义Equals ,您始终可以为接口类型定义自定义 Equal 比较器。

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;
   } 
} 

Then use the Enumerable.Contains extension method that accepts an IEqualityComparer<T> rather than List<T>.Contains然后使用接受IEqualityComparer<T>而不是List<T>.ContainsEnumerable.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

See this SharpLab for an example.有关示例,请参阅此SharpLab

returns false because types are different返回 false 因为类型不同

No, it returns false because it's a new instance.不,它返回false因为它是一个新实例。 Test this with the following:使用以下方法进行测试:

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

That will still return false as written.这仍然会返回false所写的。

Why is that?这是为什么? Because you didn't override the default object.Equals(object) virtual method in either of your types, and the default implementation calls object.ReferenceEquals , which returns whether the two objects have the same reference, ie if they're literally the same instance.因为您没有在任何一种类型中覆盖默认的object.Equals(object)虚方法,并且默认实现调用object.ReferenceEquals ,它返回两个对象是否具有相同的引用,即它们是否实际上相同实例。

From what I understand, you want to override object.Equals like this:据我了解,您想像这样覆盖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;
}

I fully support Blindy's answer for the corrections to the misapprehensions about how objects are compared (by memory address, absent any override of Equals/GetHashCode) it makes, and it's how I would have proposed working.我完全支持 Blindy 的回答,它纠正了关于如何比较对象的误解(通过 memory 地址,没有任何对 Equals/GetHashCode 的覆盖),这就是我建议的工作方式。

You said in a comment that you cannot override Equals for ImplX / IMyType, which is surprising but if it's a constraint that must be suffered then you can alter the way you check for whether the list contains a matching item:您在评论中说您不能覆盖 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) (在这个例子中它会返回真,因为 Val1 和 Val2 是相等的)

It depends what you mean by "contains".这取决于您所说的“包含”是什么意思。 If you mean exactly and specifically using Contains(T) , without being able to override Equals for the items, then No unless you want to extend the list and override Contains instead.. But LINQ's Any(=>) can tell you if a list contains an item, and because it's all IMyType regardless of the actual implementation, you can do this:如果您的意思是准确且具体地使用Contains(T) ,而无法覆盖项目的 Equals ,则否,除非您想扩展列表并覆盖 Contains 。但是 LINQ 的 Any(=>) 可以告诉您列表是否包含一个项目,因为无论实际实现如何,它都是 IMyType,您可以这样做:

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

You could bake this into an inherited list as an override to Contains too, if you like..如果您愿意,您也可以将其烘焙到继承列表中作为包含的覆盖。

yes.是的。 You can use Reflection.您可以使用反射。 There are better methods that this but you can take it as sample.有更好的方法,但您可以将其作为示例。

    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;
    }

Use this method for check if object are in list.使用此方法检查 object 是否在列表中。

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

And add in List:并添加列表:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 合并实现相同接口的两个类列表 - Merge two lists of classes implementing the same interface c#类的工厂实现相同的接口 - c# factory of classes implementing the same interface 为实现相同接口的类创建默认方法 - Create default method for classes implementing the same interface 如何比较包含 collections 条记录的记录而不实施相等比较 - How to compare records containing collections of records without implementing equality comparisons ListBox,实现相同接口的不同对象的列表 - ListBox, list of different objects implementing the same interface 如何使用 StructureMap 在构造函数中注入实现相同通用接口的所有类? - How to inject all classes implementing the same generic interface in the constructor using StructureMap? 如何使Simple Injector对同一接口使用不同的具体类,但使用不同的类 - How can I cause Simple Injector to use different concrete classes for the same interface, but with different classes 记录同一接口的两个不同类 - Logging for two different classes of the same interface 具有相同接口但属性类型不同的类 - Classes with the same interface but different types for a property Unity - 为同一个接口注入不同的类 - Unity - Inject different classes for the same interface
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM