为只读结构实现相等的最佳实践是什么?

Ryo*_*sai 5 c# struct c#-7.2

我去年刚开始用 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 位槽组成,因此复制在这里实际上可能不是一个大问题,但这只是一个简单的说明性示例。

Mar*_*ell 2

不过,上面的内容很好:请注意,in只读值类型的优点仅在大小不小的类型中真正发挥作用;对于两个整数,您可能想得太多了。

您无法消除在场景中使用按值传递的需要IEquatable<T>,因为 API 就是这样定义的。

还值得注意的是,这种in用法可能会导致很难从 C# 以外的语言使用此 API;例如,VB对此的支持很差。这是否重要取决于您的目标受众。