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