繁体   English   中英

从 C# 使用时 F# 记录类型的相等性

[英]Equality of F# record Types when consumed from C#

我在 F# 库中有以下记录类型:

type Rating = {
        Id : string
        AverageRating : decimal
        ValueRange : int
        Label : string }

正如预期的那样,以下测试在 F# 测试库中通过:

[Fact]
let ``ShouldConsiderTwoInstancesOfAClassToBeTheSame`` () =

    let a = {Rating.Id = "id"
             AverageRating = 4.3m
             ValueRange = 10
             Label = "label"}

    let b = {Rating.Id = "id"
             AverageRating = 4.3m
             ValueRange = 10
             Label = "label"}

    a = b |> should equal true
    a.Equals b |> should equal true
    a <> b |> should equal false
    System.Object.ReferenceEquals(a, b) |> should equal false

但是,C# 中的以下测试失败了......前两个断言都失败了,尽管第三个断言通过了:

    [Test]
    public void ShouldConsiderTwoInstancesOfAClassToBeTheSame()
    {
        var a = new Rating("id", 4.3m, 10, "label");
        var b = new Rating("id", 4.3m, 10, "label");

        Assert.True(a == b);
        Assert.False(a != b);
        Assert.True(a.Equals(b));
        Assert.False(ReferenceEquals(a, b));
    }

在仅使用运算符从 C# 消费时,有没有办法获得记录类型提供的开箱即用的结构相等性,或者您是否需要调用 Equals() ?

在惯用的 C# 中,引用类型预计不会对结构相等性进行正面测试。 IEquatable作为契约具有更多的语义意义。

默认情况下,相等意味着引用相等 F# 在这方面完全不同 - 它使用结构相等进行比较。 如果您查看 F# 为a = b所做的事情,它会调用

a.Equals(b, GenericEqualityComparer);

但是,对于实现了==运算符的类型,C# 编译器知道选择运算符的方法。

push.0 //ldloc, ldfld, etc.
push.1
call bool Rating::op_Equality(valuetype Rating, valuetype Rating)

a == b通常发生的情况是:

push.0
push.1
ceq

ceq比任何方法调用替代方案都快得多,并且是默认的相等比较,它为您提供引用相等。

其他核心 .NET 类型可能没有显式实现==!= ,但 JIT 具有特定的实现细节来对这些内部类型(有符号、fp 等)执行相等比较,因此它们仍然正确等同。

如果==比较在语义上对您很重要,您可以自己实现运算符:

type Rating = {
    Id : string
    AverageRating : decimal
    ValueRange : int
    Label : string 
} with
    static member op_Equality (a: Rating, b: Rating) =
      a.Equals b
    static member op_Inequality (a: Rating, b: Rating) =
          not (a.Equals b)

并且您的测试应该可以再次运行。

为了让==在 C# 中工作,有问题的类需要实现 == 运算符

bool operator ==(Rating a, Rating b) => ...

如果查看生成的用于评级的 IL 代码,您会看到它实现了IEquatable<T>, IComparable<T>, GetHashCode()等,但我没有看到运算符实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM