提问人:Nigel 提问时间:9/28/2021 最后编辑:Nigel 更新时间:9/29/2021 访问量:3564
如何在不实现相等比较的情况下比较包含记录集合的记录
How to compare records containing collections of records without implementing equality comparisons
问:
C# 9 引入了记录,除其他好处外,还可以非常轻松地进行比较。有没有办法利用该功能来比较由其他记录集合组成的记录?
例如:
record Foo
{
string Name {get; set;}
List<Bar> Bars {get; set;}
public Foo(string name, params int[] values)
{
Name = name;
Bars = values.Select(v => new Bar(v)).ToList();
}
}
record Bar
{
int Value {get; set;}
public Bar(int value) => Value = value;
}
代码中的其他位置:
var foo1 = new Foo("Hi",1,2,3);
var foo2 = new Foo("Hi",1,2,3);
return foo1 == foo2; // I want this to return true
顺便说一句,我不是在寻找这段特定代码的解决方案。我知道我可以覆盖运算符或实现 ,等等。 我的目标是利用内置功能,这样我就不必在每次想要比较由数据容器集合组成的数据容器时都实现自己的方法。有没有办法做到这一点?==
IComparable<Foo>
谢谢!
答:
1赞
Felk
9/28/2021
#1
不幸的是,没有“好”的方法可以做你想做的事。您通常有以下两种选择之一:
- 手动实现记录的相等性,在本例中,对列表使用
SequenceEqual
,或者 - 仅使用具有值语义的类。
对于后者,您可以为列表编写一个包装类,例如,如“具有集合属性的记录类型和具有值语义的集合”的回答中所述,然后仅在记录中使用该类。
2赞
Nigel
9/29/2021
#2
我实际上找到了一个不错的解决方案。您可以扩展到覆盖和List<T>
Equals
GetHashCode
public class ValueEqualityList<T>:List<T>
{
private readonly bool _requireMathcingOrder;
public ValueEqualityList(bool requireMatchingOrder = false) => _requireMathcingOrder = requireMatchingOrder;
public override bool Equals(object other)
{
if (!(other is IEnumerable<T> enumerable)) return false;
if(!_requireMathcingOrder)return enumerable.ScrambledEquals(this);
return enumerable.SequenceEqual(this);
}
public override int GetHashCode()
{
var hashCode = 0;
foreach (var item in this)
{
hashCode ^= item.GetHashCode();
}
return hashCode;
}
}
Foo
成为:
record Foo
{
string Name {get; set;}
List<Bar> Bars {get; set;}
public Foo(string name, params int[] values)
{
Name = name;
//this is the line that changed
Bars = new ValueEqualityList<Bar>(values.Select(v => new Bar(v)));
}
}
这使用了一些帮助程序代码:
static class EnumerableExtensions
{
/// <summary>
/// Returns true if both enumerables contain the same items, regardless of order. O(N*Log(N))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static bool ScrambledEquals<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
var counts = first.GetCounts();
foreach (var item in second)
{
if (!counts.TryGetValue(item, out var count)) return false;
count -= 1;
counts[item] = count;
if (count < 0) return false;
}
return counts.Values.All(c => c == 0);
}
public static Dictionary<T, int> GetCounts<T>(this IEnumerable<T> enumerable)
{
var counts = new Dictionary<T, int>();
foreach (var item in enumerable)
{
if (!counts.TryGetValue(item, out var count))
{
count = 0;
}
count++;
counts[item] = count;
}
return counts;
}
}
下一个:按引用调用线程 (.Net)
评论
¯\_(ツ)_/¯
return foo1 == foo2;