【问题标题】:Defining static classes in F#在 F# 中定义静态类
【发布时间】:2012-10-17 14:36:22
【问题描述】:

是否可以在 F# 中定义包含可重载成员的静态类? let 模块绑定不能被重载,即使它们被编译成静态类中的静态静态成员。

type declerations 可以包含静态成员,但不知道是否可以将类型本身设为静态。

我目前的解决方案是使用私有构造函数定义 type 并使用它。我想知道是否有一种方法可以根据需要定义静态类型。

【问题讨论】:

    标签: .net static f#


    【解决方案1】:

    The F# Component Design Guidelines 对此进行了解释。

    [<AbstractClass; Sealed>]
    type Demo =
        static member World = "World"
        static member Hello() = Demo.Hello(Demo.World)
        static member Hello(name: string) = sprintf "Hello %s!" name
    
    let s1 = Demo.Hello()
    let s2 = Demo.Hello("F#")
    

    仍然可以定义实例方法,但是在没有可用的构造函数时不能实例化类。

    编辑 2021 年 1 月:查看 Abel 的评论和链接的问题。 Joel Mueller 的回答似乎是迄今为止最好的建议,但未来情况可能会发生变化。

    【讨论】:

    • 没有 AbstractClass 似乎也能正常工作,我为什么要把它留在里面?
    • ...防止let foo = new Demo()
    • 不,当没有构造函数时,无论如何你都不能这样做。去掉 AbstractClass,你还是不行。
    • 从 VS2019 开始,包括私有构造函数实际上会发出一个构造函数,这大概是不可用的。所以,这个答案是合乎需要的:它产生一个密封的静态类,当反编译时看起来像这样(C#):public static class Demo { ...。省略 AbstractClass 会导致它不是是一个静态类。如果您正在与另一种语言进行互操作,这可能会更明显。
    • @bent,在 F# 中你仍然不能这样做,但是如果你的代码要被其他语言使用,你可以创建一个类的实例, 除非您还包括AbstractClass 属性。另请参阅此错误报告:github.com/dotnet/fsharp/issues/8093
    【解决方案2】:

    正如 Robert Jeppeson 所指出的,C# 中的“静态类”只是创建一个无法实例化或继承自且只有静态成员的类的简写形式。以下是在 F# 中实现这一点的方法:

    [<AbstractClass; Sealed>]
    type MyStaticClass private () =
        static member SomeStaticMethod(a, b, c) =
           (a + b + c)
    
        static member SomeStaticMethod(a, b, c, d) =
           (a + b + c + d)
    

    这可能有点矫枉过正,因为AbstractClass 和私有构造函数都会阻止您创建该类的实例,但是,这就是 C# 静态类所做的 - 它们被编译为抽象类带有私有构造函数。 Sealed 属性阻止您从此类继承。

    如果您像在 C# 中那样添加实例方法,这种技术不会导致编译器错误,但从调用者的角度来看没有区别。

    【讨论】:

    • 请注意,如果省略默认构造函数,则无法从 F# 创建类的实例,这意味着它的行为类似于静态类。但是,除非您还添加属性 AbstractClass 和 Sealed,否则 C# 不会这样看(另请参阅此错误报告:github.com/dotnet/fsharp/issues/8093)。
    【解决方案3】:

    我认为这里的问题是试图将 F# 变成 C#。如果问题不能以非命令方式解决,请使用 C# 或编写面向对象的库并在 F# 中使用。

    【讨论】:

    • F# 包含许多命令式和面向对象的功能,完全适合您需要的情况。
    【解决方案4】:

    我不确定是否存在静态类。我相信,C# 中类级别的“静态”是在 2.0 中引入的,主要是为了方便(避免私有构造函数和编译时检查不存在实例成员)。您无法检查类型并得出结论它是静态的:http://msdn.microsoft.com/en-us/library/system.reflection.typeinfo.aspx

    更新: MSDN 声明一个静态类是一个密封的并且只有静态成员的类: http://msdn.microsoft.com/en-us/library/79b3xss3(v=vs.80).aspx

    所以,你现在正在做的就是做它的方法。

    【讨论】:

      【解决方案5】:

      在 F# 中没有定义 static 类型的工具。

      第一种选择是定义一个模块,但它缺乏重载函数的能力(这是你所追求的)。第二种选择是用静态成员声明一个普通类型。

      关于第二种方法,这正是 the accepted answer 对您的旧问题所描述的。我重构了代码以更容易地解释它。首先,定义了一个虚拟的单案例可区分联合:

      type Overloads = Overloads
      

      其次,利用静态成员可以重载的事实:

      type Overloads with
          static member ($) (Overloads, m1: #IMeasurable) = fun (m2: #IMeasurable) -> m1.Measure + m2.Measure 
          static member ($) (Overloads, m1: int) = fun (m2: #IMeasurable) -> m1 + m2.Measure
      

      第三,使用inline关键字将这些重载方法的约束传播到let-bounds:

      let inline ( |+| ) m1 m2 = (Overloads $ m1) m2
      

      当您能够使用此方法重载 let-bounds 时,您应该创建一个包装模块来保存这些函数并标记您的类型 private。

      【讨论】:

      • 我在问是否有一种方法来定义静态类型,并且我知道我可以通过简单地使用带有私有构造函数的非静态类型来规避这个问题。这个解决方案有些复杂,并没有真正回答这个问题。我的意思是,结果是一个可以实例化的类。您可以编辑答案并添加您需要限制对Overloads 的工会成员的访问,但这仍然会非常复杂。
      • 是的,你可以隐藏联合大小写type Overloads = private | Overloads。静态类在 F# 中并不是真正有用,除了重载目的。我认为你最好的选择是使用私有构造函数。
      猜你喜欢
      • 2010-09-08
      • 2010-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多