F#中的C#对象初始化语法

15
请注意:此问题与这个问题不同。 我最近遇到了一些我之前没有遇到过的C#语法: 是否有办法在F#中实现这个?
class Two
{
    public string Test { get; set; }
}

class One
{
    public One()
    {
        TwoProperty = new Two();
    }
    public Two TwoProperty { get; private set; }
}

var test = new One(){ TwoProperty = { Test = "Test String" }};

(请注意,当setter为private时,在初始化器中初始化TwoProperty - 它是在存储在TwoProperty中的对象上设置属性,但不是在属性中存储Two的新实例)

编辑: 最近我在monotouch的构造函数中发现了一些C#代码,就像这样:

nameLabel = new UILabel {
    TextColor = UIColor.White,
    Layer = {
        ShadowRadius = 3,
        ShadowColor = UIColor.Black.CGColor,
        ShadowOffset = new System.Drawing.SizeF(0,1f),
        ShadowOpacity = .5f
    }
};

我所能想到的最好的F#翻译大概是这样的:

let nameLabel = new UILabel ( TextColor = UIColor.White )
do let layer = nameLabel.Layer
   layer.ShadowRadius <- 3.0f
   layer.ShadowColor <- UIColor.Black.CGColor
   layer.ShadowOffset <- new System.Drawing.SizeF(0.0f,1.0f)
   layer.ShadowOpacity <- 0.5f  

这并不糟糕,但是由于重复的layer引用,它具有更多的噪音,并且更加命令式而不是声明性。


展示一下你目前为止尝试过的内容。你觉得有哪些难点? - Eric Lippert
这是C#的一个记录特性吗?在此情况下允许调用私有setter似乎像是一个bug。 - Daniel
@Daniel,它并没有调用私有的setter。它是在设置包含在setter中的对象的属性。请参考F#翻译以了解C#正在做什么。 - N_A
我明白了。抱歉,我看得太快了。我认为在F#中这是不可能的。 - Daniel
3个回答

6
我认为 F# 在初始化过程中不允许设置嵌套属性。如果您是 API 的作者,一个变通方法是将整个对象传递给构造函数。这样可以消除在不同可访问性下使用 getter 和 setter 的需求,并使 F# 代码更加简洁。
type Two() =
    member val Test = "" with get, set

type One(twoProperty) =
    member val TwoProperty = twoProperty

let test = One(Two(Test="foo"))

正如你在评论中提到的那样,你可以创建一个帮助函数,接受各种属性作为可选参数。类型扩展非常适合这个功能:

type UILayer with
    member this.Configure(?shadowRadius, ?shadowColor, ?shadowOffset, ?shadowOpacity) = 
        this.ShadowRadius <- defaultArg shadowRadius this.ShadowRadius
        this.ShadowColor <- defaultArg shadowColor this.ShadowColor
        this.ShadowOffset <- defaultArg shadowOffset this.ShadowOffset
        this.ShadowOpacity <- defaultArg shadowOpacity this.ShadowOpacity

let nameLabel = UILabel(TextColor=UIColor.White)
nameLabel.Layer.Configure(
    shadowRadius=3.0f, 
    shadowColor=UIColor.Black.CGColor, 
    shadowOffset=SizeF(0.0f, 1.0f), 
    shadowOpacity=0.5f)

很遗憾,我正在构建的类是一个UILabel,它是Cocoa Touch框架中的一个类。 - N_A

4

将构造函数封装到单独的初始化函数中是否有意义?

let layerInit layer radius color offset opacity =
    do
       layer.ShadowRadius <- radius
       layer.ShadowColor <- color
       layer.ShadowOffset <- offset
       layer.ShadowOpacity <- opacity
    layer // I do this in case you want to use this fluently or pass in new Layer()

然后在你的代码中使用它:
let nameLabel = new UILabel ( TextColor = UIColor.White )
layerInit nameLabel.Layer 3.0f UIColor.Black.CGColor new System.Drawing.SizeF(0.0f,1.0f) 0.5f |> ignore

我喜欢这个想法。如果我要使用这个框架进行大量开发,我可能会创建一个带有所有可用属性的可选参数的辅助函数,以便参数可以命名。 - N_A

1

F# 4.0推出了一个新功能,可能对封装@plinth的回答很有用。它允许创建扩展属性,并在构造函数中初始化。


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