出于单元测试的目的,我经常需要覆盖 Equals
和 GetHashCode
方法。在此之后,我的类(class)开始看起来像这样:
public class TestItem
{
public bool BoolValue { get; set; }
public DateTime DateTimeValue { get; set; }
public double DoubleValue { get; set; }
public long LongValue { get; set; }
public string StringValue { get; set; }
public SomeEnumType EnumValue { get; set; }
public decimal? NullableDecimal { get; set; }
public override bool Equals(object obj)
{
var other = obj as TestItem;
if (other == null)
{
return false;
}
if (object.ReferenceEquals(this, other))
{
return true;
}
return this.BoolValue == other.BoolValue
&& this.DateTimeValue == other.DateTimeValue
&& this.DoubleValue == other.DoubleValue // that's not a good way, but it's ok for demo
&& this.EnumValue == other.EnumValue
&& this.LongValue == other.LongValue
&& this.StringValue == other.StringValue
&& this.EnumValue == other.EnumValue
&& this.NullableDecimal == other.NullableDecimal;
}
public override int GetHashCode()
{
return this.BoolValue.GetHashCode()
^ this.DateTimeValue.GetHashCode()
^ this.DoubleValue.GetHashCode()
^ this.EnumValue.GetHashCode()
^ this.LongValue.GetHashCode()
^ this.NullableDecimal.GetHashCode()
^ (this.StringValue != null ? this.StringValue.GetHashCode() : 0);
}
}
虽然这并不难,但一次又一次地在 Equals
和 GetHashCode
中维护相同字段的列表会变得乏味且容易出错。有什么方法可以只列出一次用于相等性检查和哈希码功能的文件吗? Equals 和 GetHashCode 应该按照这个设置列表来实现。
在我的想象中,这样的设置列表的配置和用法可能看起来像
public class TestItem
{
// same properties as before
private static readonly EqualityFieldsSetup Setup = new EqualityFieldsSetup<TestItem>()
.Add(o => o.BoolValue)
.Add(o => o.DateTimeValue)
// ... and so on
// or even .Add(o => o.SomeFunction())
public override bool Equals(object obj)
{
return Setup.Equals(this, obj);
}
public override int GetHashCode()
{
return Setup.GetHashCode(this);
}
}
有一种方法可以在 java 中自动实现 hashCode
和 equals
,project lombok例如。我想知道是否有任何东西可以减少可用于 C# 的样板代码。
最佳答案
我认为有可能在 C# 中实现与 Lombok 几乎相同的东西,但目前我并没有那么雄心勃勃。
我相信这就是您所追求的(与您描述的几乎完全一样)。此实现确实将所有值类型装箱到对象中,因此它不是最有效的实现,但它应该足以满足您的单元测试目的。
public class EqualityFieldsSetup<T>
where T : class
{
private List<Func<T, object>> _propertySelectors;
public EqualityFieldsSetup()
{
_propertySelectors = new List<Func<T, object>>();
}
public EqualityFieldsSetup<T> Add(Func<T, object> propertySelector)
{
_propertySelectors.Add(propertySelector);
return this;
}
public bool Equals(T objA, object other)
{
//If both are null, then they are equal
// (a condition I think you missed)
if (objA == null && other == null)
return true;
T objB = other as T;
if (objB == null)
return false;
if (object.ReferenceEquals(objA, objB))
return true;
foreach (Func<T, object> propertySelector in _propertySelectors)
{
object objAProperty = propertySelector.Invoke(objA);
object objBProperty = propertySelector.Invoke(objB);
//If both are null, then they are equal
// move on to the next property
if (objAProperty == null && objBProperty == null)
continue;
//Boxing requires the use of Equals() instead of '=='
if (objAProperty == null && objBProperty != null ||
!objAProperty.Equals(objBProperty))
return false;
}
return true;
}
public int GetHashCode(T obj)
{
int hashCode = 0;
foreach (Func<T, object> propertySelector in _propertySelectors)
{
object objProperty = propertySelector.Invoke(obj);
if (objProperty != null)
hashCode ^= objProperty.GetHashCode();
}
return hashCode;
}
}
关于c# - 有没有办法减少 Equals 和 GetHashCode 中的样板代码量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17558135/