我去年刚开始用 C# 编程,我还在学习这门语言。我有一个关于readonly struct类型和相等比较方法的问题。
在 C# 中创建结构时,我知道实现IEquatable通常被认为是最佳实践,因为默认的基于反射的比较非常慢。我还了解到,在 C# 7.2 及更高版本中,我们可以定义readonly结构,对于这些类型,我们还可以使用in参数来避免不必要的复制。
由于结构通常被定义为不可变的只读类型,我认为为只读结构定义 Equals 方法并不罕见。
然而,鉴于上述事实,我想知道是否有一种有效的方法来为它们实现相等比较方法。我的观点是,这些相等方法和运算符实际上都不需要修改参数,因此我想以in某种方式利用参数来节省不必要的复制。
以下是我在这样做的尝试:
public readonly struct Point : IEquatable<Point>
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
// Explicitly implementing IEquatable<Point> and delegating to an Equals method taking in param.
bool IEquatable<Point>.Equals(Point other) => Equals(other);
public bool Equals(in Point other) => X == other.X && Y == other.Y;
public override bool Equals(object? obj) => obj is Point other && Equals(other);
public static bool operator ==(in Point left, in Point right) => left.Equals(right);
public static bool operator !=(in Point left, in Point right) => !left.Equals(right);
public override int GetHashCode() => HashCode.Combine(X, Y);
public override string ToString() => $"Point({X}, {Y})";
}
Run Code Online (Sandbox Code Playgroud)
以上当然有效,但我认为这不是完美的解决方案,因为如果通过IEquatable接口调用它仍然需要复制。请注意,我不能仅IEquatable使用inparam隐式实现,因为采用修饰符的 Equal 方法被认为具有不同的签名并被视为重载。
是否有已知的最佳实践来正确实施?
我真正想知道的是,是否有已知的最佳实践和模式来有效地实现此类只读结构的相等性。特别是我对正确利用in参数修饰符来实现 Equality 比较方法的方法很感兴趣。
目前在网上还没有找到满意的答案,也查了一些核心库的源码。例如,System.DateTime现在被定义为只读结构,它相当大,但这里没有使用参数。(现有类型可能需要保持兼容性,但我知道他们经常需要妥协。)
请注意,上面定义的 Point 结构很小,仅由两个 32 位槽组成,因此复制在这里实际上可能不是一个大问题,但这只是一个简单的说明性示例。
不过,上面的内容很好:请注意,in只读值类型的优点仅在大小不小的类型中真正发挥作用;对于两个整数,您可能想得太多了。
您无法消除在场景中使用按值传递的需要IEquatable<T>,因为 API 就是这样定义的。
还值得注意的是,这种in用法可能会导致很难从 C# 以外的语言使用此 API;例如,VB对此的支持很差。这是否重要取决于您的目标受众。