Record Equals或GetHashCode抛出NullReferenceException

Wes*_*ser 6 f# nullreferenceexception .net-4.5

我有一些看起来像这样的记录:

[<DataContract>]
type Rec1 = {
    [<DataMember>] mutable field1 : int;
    [<DataMember>] mutable field2 : string;
}

[<DataContact>]
type Rec2 = { 
    [<DataMember>] mutable field3 : Rec1;
    [<DataMember>] mutable field4 : int;
}
Run Code Online (Sandbox Code Playgroud)

DataContactJsonSerializer用来将JSON反序列化为这个结构.这是一个有效的JSON值:

{ "field3": null, "field4": 1 }
Run Code Online (Sandbox Code Playgroud)

这意味着在运行时,field3null/Unchecked.defaultOf<_>.在Visual Studio 2010中,此测试工作正常:

(deserialize<Rec2> "{ field3: null, field4: 1 }") = { field3 = Unchecked.defaultOf<_>; field4 = 1 } //true
Run Code Online (Sandbox Code Playgroud)

在Visual Studio 2013中,相同的代码抛出NullReferenceException:

at Rec2.Equals(Rec2 obj) 
Run Code Online (Sandbox Code Playgroud)

偷看ILSpy中的代码,我看到这是生成的:

if(this != null)
{
    return obj != null && this.field3.Equals(obj.field3) && this.field4.Equals(obj.field4);
}
return obj == null;
Run Code Online (Sandbox Code Playgroud)

所以问题是编译器假定field3从不为null,因为DataContractJsonSerializer将值设置为不是这种情况null.我已尝试应用该AllowNullLiteral属性,Rec1但不允许F#记录将该属性应用于它们.我怎么能告诉编译器字段可以是null或重新构造我的类型以允许它工作?

Jar*_*Par 8

F#正在生成类型,假设它只能在F#中使用,null而不可能.因为[<AllowNullLiteral>]不允许在该元素上我不认为有一种方法来控制代码gen,因此它将解释这种可能性.我可以想到两种方法来解决这个问题

  1. 实现自定义相等Rec2而不是默认的结构相等.这意味着你必须自己编写相等的方法,这是不幸的,但它应该让你考虑到的可能性null
  2. 更改代码以生成struct实例而不是class.这消除了null任何CLR用例的可能性.这将要求您从记录定义移动到完整类型