[英]Is there a way to reduce amount of boilerplate code in Equals and GetHashCode?
出於單元測試的目的,我經常不得不重寫Equals
和GetHashCode
方法。 在此之后,我的課程開始看起來像這樣:
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
,例如項目 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;
}
}
我做了一些研究,發現了幾個不是我想要的組件:
GetHashCode()
並且對我來說太重了。GetHashCode()
組合。Emit
有點矯枉過正。還有一些相關的討論:
到目前為止,明確配置成員列表的想法似乎是獨一無二的。 我實現了自己的庫https://github.com/msugakov/YetAnotherEqualityComparer 。 它比 TylerOhlsen 建議的代碼要好,因為它不會將提取的成員裝箱,而是使用EqualityComparer<T>
來比較成員。
現在代碼看起來像:
public class TestItem
{
private static readonly MemberEqualityComparer<TestItem> Comparer = new MemberEqualityComparer<TestItem>()
.Add(o => o.BoolValue)
.Add(o => o.DateTimeValue)
.Add(o => o.DoubleValue) // IEqualityComparer<double> can (and should) be specified here
.Add(o => o.EnumValue)
.Add(o => o.LongValue)
.Add(o => o.StringValue)
.Add(o => o.NullableDecimal);
// property list is the same
public override bool Equals(object obj)
{
return Comparer.Equals(this, obj);
}
public override int GetHashCode()
{
return Comparer.GetHashCode(this);
}
}
MemberEqualityComparer 還實現了IEqualityComparer<T>
並遵循其語義:它可以成功比較對於引用類型和 Nullables 可能為null
的default(T)
。
更新:有一些工具可以解決創建基於成員的IEqualityComparer<T>
的相同問題,但這些工具也可以提供復合IComparer<T>
!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.