【问题标题】:Accessing let bound fields from static members从静态成员访问 let 绑定字段
【发布时间】:2011-03-07 03:36:52
【问题描述】:

有没有办法从静态成员访问 let 绑定字段?以下给出了指示的错误:

type Foo(x) =
    let x = x
    static member test() =
        let foo = Foo(System.DateTime.Now.Month)
        printfn "%A" foo.x //the field, constructor or member 'x' is not defined
        ()

而私有显式字段确实允许静态成员访问:

type Bar =
    val private x:int
    new(x) = { x=x }
    static member test() =
        let Bar = Bar(System.DateTime.Now.Month)
        printfn "%A" Bar.x
        ()

文档http://msdn.microsoft.com/en-us/library/dd469494.aspx 声明“显式字段不适合常规使用”,但从静态成员访问私有实例字段肯定是常规场景。此外,我不相信您可以在主构造函数中设置显式字段,这意味着即使需要从静态成员访问一个私有实例字段,您的所有字段都必须移至显式字段,并且您不能再使用主构造函数——要么全有,要么全无。

作为实际示例,您实际上希望从静态成员访问私有实例字段,请考虑大整数实现:BigInteger 类将是不可变的,因此大整数的内部表示将保留为私有实例字段(我们称之为data)。现在,假设您认为Add(other) 实例方法不适用于不可变数据结构,并且您只想实现静态Add(lhs,rhs) 方法:在这种情况下,您需要能够访问lhs.datarhs.data .

【问题讨论】:

    标签: f#


    【解决方案1】:

    我认为你不能这样做......事实上,你也不能从其他实例访问 let-bound 值:

    type Foo() =
      let x = 3
      member this.Test(f:Foo) =
        f.x // same error
    

    一般来说,如果您需要从它所属的实例外部访问这样的值,您可能应该创建一个私有属性来获取该值,或者改用一个私有字段。

    更新 这由section 8.6.2 of the spec 覆盖。特别是:

    实例“let”绑定在词法范围内(因此隐式私有)到正在定义的对象。

    也许 F# 团队的某个人会给出明确的答案,解释为什么该语言会以这种方式运行。不过,我能想到几个可能的原因:

    1. let-bound 值甚至可能不作为字段存在(例如,再次从规范中,如果值不是语法函数、不可变且未使用,则 let 绑定将由构造函数的局部表示在任何功能或成员中”)
    2. 这似乎与该语言中其他地方的 let 绑定的行为一致。请参阅我在下面包含的大致等效的类和记录定义的示例(因为我似乎无法正确格式化有序列表中的代码块......)
    3. 与许多其他语言相比,这提供了更细粒度的封装级别 - 被定义对象的本地绑定。通常,其他实例不需要访问这些绑定,在这种情况下最好不要公开它们。
    4. 如果您想要您的类的其他实例(或从静态方法中)可以访问的东西,有一种简单的方法可以做到这一点 - 创建一个私有字段或属性,它的好处是明确表达您的意图,即值可以从您所在的实例外部访问。

    如前所述,这里有一个大致等价的类定义和创建记录的方法:

    type MyClass(i:int) =
      let j = i * i
      member this.IsSameAs(other:MyClass) = 
        false // can't access other.j here
    
    type myRecord = { isSameAs : myRecord -> bool }
    let makeMyRecord(i:int) =
      let j = i * i
      { isSameAs = (fun r -> false) } //obviously, no way to access r.j here
    

    由于 F# 中的构造函数在概念上类似于任何其他返回类型实例的函数(例如,它们可以在不使用 new 的情况下调用),因此调用 MyClass 5 在概念上类似于调用 makeMyRecord 5。在后一种情况下,我们显然不希望有任何方法可以从记录的另一个实例访问 j 的本地 let 绑定。因此,在前一种情况下我们也无权访问绑定是一致的。

    【讨论】:

    • 是否有任何技术或逻辑原因让绑定字段在这方面与显式私有字段有任何不同?
    • @Stephen - 请参阅我的更新以推测为什么可能采用这种方法。
    • 谢谢。它们是词法范围的事实是否限制了编译器在这些场景中将它们作为字段发现的能力(“隐式私有”=隐藏)?
    • 我心目中的关键部分是,即使使用了“let”语法,从语义上讲,我希望字段访问(当它们被编译为字段时)全面一致(对不起如果我滥用条款)。另外,关于您的第二个猜测,模块是否为您提供路径限定的访问权限来让绑定值(例如 List.fold)?
    • @Stephen - 关于你的第一个问题,请注意规范说它们对正在定义的 object 隐含私有,所以这就是为什么它们在访问其他对象时被隐藏属于同一类。至于你的第二个问题,你当然是对的;我会修改我的答案以反映这一点。
    【解决方案2】:

    然而,访问 let 绑定的字段来自 静态成员当然是例行公事 场景

    你在这里是什么意思?对应的 C# 场景是什么(带有示例)?

    注意这是合法的:

    type Foo() =
        let x = 4
        member this.Blah = x + 1
        member private this.X = x
        static member Test(foo:Foo) =
            foo.X
    

    也就是说,您可以将 let-bound 值公开为私有成员,静态可以读取/使用。

    【讨论】:

    • 好吧,我真正的意思是访问私有实例字段/成员/等。来自同一类的静态成员的类是大多数 oo 编程语言所允许的常见场景……即使是 F#,当您使用显式“val”私有实例字段时,您也可以这样做。我的评论是,我希望主构造函数 / let 绑定语法是这样的:当 let 绑定字段被编译为字段时,我希望它们可以通过与普通私有字段相同的规则进行访问。
    • 谢谢,我正在考虑采用这种方法,但我遇到的主要问题是我认为 X 是属性而不是字段(我真的更愿意使用字段)。跨度>
    • @Stephen - 您不想使用私有财产的任何特殊原因? CLR 可能会在运行时内联任何琐碎的属性,因此性能通常应该不是问题...
    • @kvb - 这就是特殊原因!很棒的信息让我放心。不过,这种方法的另一个问题是,有时我会在范围内同时拥有 x 和 X 。此外,这很 hacky:我可能有五个 let 绑定字段,但只需要从静态成员访问其中一个,在这种情况下,为该单个字段创建属性包装器的唯一目的是绕过限制(如果我以后需要访问其他人,我必须回去添加属性包装器)。
    • 从另一面来看,你所看到的“限制”可以说是“改进的封装”(如果我想编写一个只打算使用公共 API 的静态成员呢?你不能拥有编译器会验证你在 C# 中是否正确)。
    猜你喜欢
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 2021-08-29
    • 2014-04-26
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    相关资源
    最近更新 更多