如何在 F# 中使用字符串类型列表的自动 属性 声明 class?

How to declare class with auto property of type list of strings in F#?

我很难理解我应该如何在 F# 中定义 class,因为我的属性还没有声明值。相当于 c# 代码的东西:

class Entity 
{
      public string Name { get; set; }
      public List<string> Favorites { get; set;}
}

我只能声明类型定义为

type Entity = 
    member val Name = "" with get, set

当我分配 null 时,Name 的类型将为 null。

如评论中所述,F# 中不推荐使用 null 值,因此最好尽可能避免使用它们。默认情况下,F# 类型也不允许 null,因此如果您使用不可变的 F# list 类型,那么您最好的选择是将 属性 初始化为一个空列表:

type Entity() = 
    member val Name = "" with get, set
    member val Favorities : list<string> = [] with get, set

请注意,我添加了一个类型注释 list<string>,因为编译器无法仅从代码中推断空列表包含字符串,否则您最终会得到 list<obj>

C# List 类型在 F# 中缩写为 ResizeArray 并且由于这是 .NET 类型,因此可以将此类型的属性初始化为 null:

type Entity() = 
    member val Name = "" with get, set
    member val Favorities : ResizeArray<string> = null with get, set

我想如果您正在使用一些非 F# 友好的 ORM 和类似的东西,这可能会有用,但否则,使用它可能会给您带来很多痛苦和意外错误。

最接近该 C# 代码的 F# 是这样的:

type Entity() = 
    member val Name = Unchecked.defaultof<string> with get, set
    member val Favorites = Unchecked.defaultof<string list> with get, set

但是,这是一个糟糕的 F#,这就是为什么它被故意设计得看起来很丑。 F# 不鼓励使用默认值、可空引用和变异。

因此,您应该决定一些合理的默认值:

type Entity() = 
    member val Name = "" with get, set
    member val Favorites = [] with get, set

或者您应该使用 Option 类型使值显式可选,并使用 None 作为默认值,避免在运行时出现空引用异常:

type Entity() = 
    member val Name = None : string option with get, set
    member val Favorites = None : string list option with get, set

注意:我在上面使用的 list 与 C# List 不同。

我在您的评论中看到您使用 member val 的原因是为了 Newtonsoft.Json。将它与 F# 记录一起使用曾经是一个问题,但现在它工作得很好。以下是使用 Newtonsoft.Json 10.0.3 测试的:

type Foo =
    {
        x: int
        y: float
    }

open Newtonsoft.Json

let foo = JsonConvert.DeserializeObject<Foo>("{\"x\":1,\"y\":1.2}")
printfn "%A" foo
// {x = 1;
//  y = 1.2;}

对于需要默认构造函数和可变字段的序列化库,您可以将 [<CLIMutable>] 属性放在记录上,它会提供这些(但在 F# 中不可见,以保持安全保证)。