[英]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.