F# 对象树语法

3
在C#中,可以使用非常简洁的语法构建对象树:
var button = new Button() { Content = "Foo" };

在F#中是否有一种惯用的方式来做类似的事情?

记录(Records)具有良好的语法:

let button = { Content = "Foo" }

据我所知,对象的构建似乎是一件不同的事情。通常情况下,我会编写以下代码:

let button = new Button()
button.Content <- "Foo"

甚至可以这样说:
let button =
    let x = new Button()
    x.Content <- "Foo"
    x

解决问题的一种方法是使用自定义流畅组合运算符:

// Helper that makes fluent-style possible
let inline (.&) (value : 'T) (init: 'T -> unit) : 'T =
    init value
    value

let button = new Button() .& (fun x -> x.Content <- "Foo")

有没有内置的语法可以实现这个目标,或者推荐其他方法?

2个回答

6

F#允许您在构造函数调用中设置属性,因此我认为这对您有用:

let button = Button(Content = "Foo")

太好了!我会暂时保留这个问题,以防有任何关于不那么直接的情况的建议,例如 let stackPanel = new StackPanel() .& (fun x -> x.Children.Add(...) ... ),然后我将把这个作为答案关闭。 - Bent Rasmussen
我们没有你所描述的开放和关闭的概念。如果我们关闭一个问题,那是因为它存在问题,除非重新开放,否则就不能再添加答案了。你所描述的实际上是你会接受最佳答案,并且你可以随时改变对哪个答案最佳的看法。 - Bent Tranberg
我应该写“已接受”或“标记”。对于造成的混淆,我很抱歉。 :-) 同时也很好知道更改已接受答案是可以的。 - Bent Rasmussen

3
在C#中,这种漂亮的语法被称为对象初始化器,然后()可以被移除(1)。为了在初始化之后“内联”地更改对象(流畅风格),我喜欢使用一个类似于你的.&操作符的With()扩展方法(2):
var button = new Button { Content = "Foo" }; // (1)

// (2)
public static T With<T>(this T @this, Action<T> update)
{
    change(@this);
    return @this;
}

var button2 = button.With(x => x.Content = "Bar")

对于那些更喜欢使用管道而不是运算符的 F# 用户,该函数可以被命名为 tap(参见 RxJs)或 tee(由 Scott Wlaschin 在 这里 中提到):

// f: ('a -> 'b) -> x: 'a -> 'a
let inline tee f x =
    f x |> ignore
    x

let button =
    Button(Content = "Foo")
    |> tee (fun x -> x.Color <- Blue)
    |> tee (fun x -> x.Content <- "Bar")

我还写过使用 C# 中的扩展方法。但我更喜欢 F# 中的“操作符”中缀样式。在大多数情况下,根据 Brian 的被接受答案,我现在可以避免使用运算符。但对于其他情况,仍然需要它。 - Bent Rasmussen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接