这可能是一个初学者的问题,但我有一个问题,我找不到现有的解决方案,希望你能在这里帮助我。
我想按值比较引用类型对象的实例,而不手动覆盖此类及其子类中的
.Equals()
方法。这是因为我不想在类中编写所有代码,尤其是它有很多子代码,因为这会给已经庞大的代码库增加很多复杂性。有没有一种方法可以比较此类(或其子类)的两个对象,而不必为每个类编写实现?
记录不是一种选择,因为它们需要保持可变。 结构是不可能的,因为我仍然希望对象是引用类型,因为它们被传递了很多。
理想情况下,我也希望能够在某些情况下仅使用“值类型比较”,同时将“引用类型比较”保留为默认值。
有没有办法在 C# 中本地执行此操作,而无需为从相关类继承的每个类编写附加函数/覆盖
.Equals()
和 .GetHash()
?
我的代码看起来有点类似于:
using System;
namespace SubwaySystem{
public abstract class Passenger {
public string name;
public int identificationNumeber;
public int age;
}
public class SingleTicketPassenger : Passenger
{
public string ticketModel;
public string ticketprice;
}
public class SubscriptionPassenger : Passenger
{
public DateTime subscriptionExpiryDate;
public int subscriptionPrice;
public string subscriptionModel;
}
public class GateHopper : Passenger
{
public string entryStation;
public string? arrestRecordID;
public bool firstOffence;
}
// many more classes inherenting from Passenger
public class Subway {
public Passenger oldestPassengerOnBoard;
public string model;
public string line;
public DateTime constructionDate;
public int speed;
public override bool Equals(object? obj)
{
var otherSubway = obj as Subway;
return this.Equals(otherSubway);
}
public bool Equals(Subway otherSubway)
{
return otherSubway != null &&
otherSubway.oldestPassengerOnBoard == oldestPassengerOnBoard &&
otherSubway.model == model &&
otherSubway.line == line &&
otherSubway.constructionDate == constructionDate &&
otherSubway.speed == speed;
}
public override int GetHashCode()
{
unchecked
{
int hash = 7;
hash = hash * 11 + oldestPassengerOnBoard.GetHashCode();
hash = hash * 11 + speed.GetHashCode();
return hash;
}
}
}
}
我想要做的是确保两个地铁不仅在最年长的乘客按引用相等时相等,而且按值相等。 (在实际的代码中,它更有意义,在这个例子中,只要与我保持一致,并假设两个人是同一个人,如果他们具有相同的属性)。乘客的哈希码当然也必须仅依赖于他们的属性。
最后我基本上会喜欢可变记录。或者类似于通用对象上的
.CompareByValue()
方法。
按值比较对象时,有很多可能的边缘情况(您只比较属性吗?只比较字段?如果字段包含对象怎么办?),但如果您只想按公共字段进行比较,您可以编写一个为所有引用类型执行此操作的扩展方法。使用 Reflection 迭代字段并获取值,然后使用值类型的
Equals
方法,或者如果是引用类型,则递归调用 CompareByValue
。
static class ComparisonExtensions
{
static public bool CompareByValue<T>(this T source, T other) where T : class
{
var fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
var lhs = field.GetValue(source);
var rhs = field.GetValue(other);
var equal = field.FieldType.IsValueType ? !lhs.Equals(rhs): lhs.CompareByValue(rhs);
if (!equal) return false;
}
return true;
}
}