如何将 LanguagePrimitives.GenericZero / get_Zero 添加到 System.String?

Posted

技术标签:

【中文标题】如何将 LanguagePrimitives.GenericZero / get_Zero 添加到 System.String?【英文标题】:How to add LanguagePrimitives.GenericZero / get_Zero to System.String? 【发布时间】:2016-05-09 13:30:12 【问题描述】:

注意:我在最后添加了很多Of interest cmets。这些并不意味着建议人们应该使用inlinestatic type parameters willy nilly,它们的存在是为了让人们不必花费数小时搜索与此问题相关的大量 SO 问题以更好地理解这些概念。

我知道当需要使函数泛型并需要零 (0) 值时,F# 提供 GenericZero。

解析为任何原始数字类型或任何类型的零值 有一个名为 Zero 的静态成员。

所以这让我相信要将GenericZero 与字符串类型一起使用,我只需添加一个名为 Zero 的静态成员。

由于System.String 是.Net 框架的一部分,修改.Net 源代码不是应该做的。但是 F# 提供了Type Extensions。

类型扩展允许您将新成员添加到先前定义的对象 输入。

F# 也提供 String module,但缺少 GenericZero。

有关创建类型扩展的优秀教程参考:Attaching functions to types。

我测试的代码:

这是在一个名为Library1的项目中

namespace Extension.Test

module Extensions = 

    type System.String with
        static member Something = "a"

        static member StaticProp
            with get() = "b"

        static member Zero
            with get() = "c"

这是在一个名为Workspace的项目中

namespace Extension.Test
module main =

    open Extensions

    [<EntryPoint>]
    let main argv = 

        let stringSomething = System.String.Something
        printfn "something: %s" stringSomething

        let staticProperty = System.String.StaticProp
        printfn "staticProperty: %s" staticProperty

        let zeroProperty = System.String.Zero
        printfn "zeroProperty: %s" zeroProperty

        let inline addTest (x : ^a) (y : ^a) : ^a =
            x + y

        let intAdd = addTest 2 LanguagePrimitives.GenericZero
        let floatAdd = addTest 2.0 LanguagePrimitives.GenericZero
//        let stringAdd = addTest "a" LanguagePrimitives.GenericZero

        printfn "intAdd: %A" intAdd
        printfn "floatAdd: %A" floatAdd
//        printfn "stringAdd: %A" stringAdd

        printf "Press any key to exit: "
        System.Console.ReadKey() |> ignore
        printfn ""

        0 // return an integer exit code

运行时输出:

something: a
staticProperty: b
zeroProperty: c
intAdd: 2
floatAdd: 2.0
Press any key to exit

所以我正在创建和访问扩展成员并使用 GenericZero 没有任何问题。

最后一部分是使用 GenericZero 作为字符串,但是取消注释该行

let stringAdd = addTest "a" LanguagePrimitives.GenericZero

导致错误:

类型'string'不支持操作符'get_Zero'

我确实检查了F# spec,但没有找到任何帮助。

我可以在类型System.String 中添加 GenericZero,是我在代码中做错了什么,还是我错过了文档中的某些内容?

TL;DR

搜索时感兴趣的问题

F# - How do I extend a type with get_Zero so I can use an existing type generically? 哪个 IMO 是一个误导性标题,应在阅读答案后更改。

What/where is get_Zero in F#'s int? 杰克有一个很好的评论:

如果从以下方面考虑,零在字符串上是没有意义的 数字;但是,在字符串上有一个零成员确实有意义 它返回空字符串,因为这会使字符串成为幺半群 在字符串连接下。

感兴趣的 F# 文档

Automatic Generalization

F# 编译器在对函数执行类型推断时, 确定给定参数是否可以是通用的。编译器 检查每个参数并确定函数是否具有 依赖于该参数的特定类型。如果没有,则 类型被推断为泛型。

Type Inference

类型推断的想法是你不必指定 F# 构造的类型,除非编译器无法确定 推断类型。

对于那些你没有明确指定的类型,编译器 根据上下文推断类型。如果类型不是,否则 指定,则推断为泛型。

Generics

F# 函数值、方法、属性和聚合类型,例如 类、记录和有区别的联合可以是通用的。通用的 构造包含至少一个类型参数,通常是 由通用构造的用户提供。通用函数和 类型使您能够编写适用于各种类型的代码 无需为每种类型重复代码。使您的代码通用可以 在 F# 中要简单,因为您的代码通常被隐式推断为 通过编译器的类型推断和自动泛化来泛型 机制。

Statically Resolved Type Parameters

静态解析的类型参数是一个类型参数,它是 在编译时而不是在运行时替换为实际类型。 它们前面有一个插入符号 (^)。

静态解析类型参数主要用于 结合成员约束,这些约束允许 您指定类型参数必须具有特定成员或 会员才能使用。没有办法创建这种 通过使用常规泛型类型参数进行约束。

在 F# 语言中,有两种不同的类型参数。 第一种是标准的泛型类型参数。这些是 用撇号 (') 表示,如 'T 和 'U。它们是等价的 到其他 .NET Framework 语言中的泛型类型参数。这 另一种是静态解析的,由插入符号表示, 如 ^T 和 ^U。

Constraints

Inline Functions

当您使用静态类型参数时, 按类型参数化的参数必须是内联的

编辑

这是一个示例,它使用GenericZero 作为用户定义的类型,而没有使用有效的扩展名,两个变体表明GenericZero 不适用于intrinsic extensionoptional extension

首先运行程序以查看GenericZero 的工作情况,然后取消注释Program.fs 中的行以查看intrinsic extensionoptional extension 的错误。

内在扩展是出现在同一 命名空间或模块,在同一个源文件中,在同一个程序集中 (DLL 或可执行文件)作为被扩展的类型。

可选扩展程序是出现在 被扩展类型的原始模块、命名空间或程序集。 当类型被检查时,内部扩展出现在类型上 反射,但可选扩展没有。可选扩展必须 在模块中,并且它们仅在模块的范围内 包含扩展是打开的。

Library1.fs 项目中Library1

namespace Extension.Test

module module001 = 

    // No extension
    type MyType01(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType01("a")        
        static member (+) (mt1 : MyType01, mt2 : MyType01) = MyType01(mt1.x + mt2.x)
        static member (+) (mt1 : MyType01, s : string) = MyType01(mt1.x + s)
        static member (+) (s : string, mt2 : MyType01) = MyType01(s + mt2.x)

        static member Zero
            with get() = MyType01("b")

    // uses intrinsic extension
    type MyType02(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType02("g")        
        static member (+) (mt1 : MyType02, mt2 : MyType02) = MyType02(mt1.x + mt2.x)
        static member (+) (mt1 : MyType02, s : string) = MyType02(mt1.x + s)
        static member (+) (s : string, mt2 : MyType02) = MyType02(s + mt2.x)

//        static member Zero
//            with get() = MyType02("h")

    // uses optional extension
    type MyType03(x: string) =
        member this.x = x

        override this.ToString() = this.x.ToString()

        static member Something = MyType03("m")        
        static member (+) (mt1 : MyType03, mt2 : MyType03) = MyType03(mt1.x + mt2.x)
        static member (+) (mt1 : MyType03, s : string) = MyType03(mt1.x + s)
        static member (+) (s : string, mt2 : MyType03) = MyType03(s + mt2.x)

//        static member Zero
//            with get() = MyType03("n")


module module002 = 

    open module001

    // intrinsic extension
    type MyType02 with

        static member Zero
            with get() = MyType02("h")

Library2.fs 在项目中Library2

namespace Extension.Test

open module001

module module003 = 

    type MyType01 with 

        static member Anything = MyType02("c")

    type MyType02 with 

        static member Anything = MyType02("i")

    // optional extension
    type MyType03 with 

        static member Anything = MyType03("p")

        static member Zero
            with get() = MyType03("n")

Program.fs 在项目中Workspace

namespace Workspace

open Extension.Test.module001
open Extension.Test.module002
open Extension.Test.module003

module main =

    [<EntryPoint>]
    let main argv = 


        let staticFromBaseType = MyType01.Something
        printfn "MyType01 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType01.Anything
        printfn "MyType01 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType01.Zero
        printfn "MyType01 zeroValue: %A" zeroValue

        let (genericZero: MyType01) = LanguagePrimitives.GenericZero
        printfn "MyType01 genericZero: %A" genericZero

        let staticFromBaseType = MyType02.Something
        printfn "MyType02 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType02.Anything
        printfn "MyType02 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType02.Zero
        printfn "MyType02 zeroValue: %A" zeroValue

//        let (genericZero: MyType02) = LanguagePrimitives.GenericZero
//        printfn "MyType02 genericZero: %A" genericZero


        let staticFromBaseType = MyType03.Something
        printfn "MyType03 staticFromBaseType: %A" staticFromBaseType

        let staticFromExtensionType = MyType03.Anything
        printfn "MyType03 staticFromExtensionType: %A" staticFromExtensionType

        let zeroValue = MyType03.Zero
        printfn "MyType03 zeroValue: %A" zeroValue

//        let (genericZero: MyType03) = LanguagePrimitives.GenericZero
//        printfn "MyType03 genericZero: %A" genericZero

        let inline addTest (x : ^a) (y : ^a) : ^a =
            x + y

        let intAdd = addTest 2 LanguagePrimitives.GenericZero
        let floatAdd = addTest 2.0 LanguagePrimitives.GenericZero
        let (myType01Add : MyType01) = addTest (MyType01("d")) LanguagePrimitives.GenericZero
//        let (myType02Add : MyType02) = addTest (MyType02("d")) LanguagePrimitives.GenericZero
//        let (myType03Add : MyType03) = addTest (MyType03("o")) LanguagePrimitives.GenericZero

        printfn "intAdd:      %A" intAdd
        printfn "floatAdd:    %A" floatAdd
        printfn "myType01Add: %A" myType01Add
//        printfn "myType02Add: %A" myType02Add
//        printfn "myType03Add: %A" myType03Add


        printf "Press any key to exit: "
        System.Console.ReadKey() |> ignore
        printfn ""

        0 // return an integer exit code

【问题讨论】:

感兴趣的:Could not extend operators in F#? 感兴趣的:How to define a type extension for T[] in F#? 感兴趣的:How can I define an extension member on an F# unit of measure? 感兴趣的:F# Extention Methods on Lists, IEnumberable, etc 感兴趣的:F#: Member constraints to help create seemingly dynamic types 【参考方案1】:

扩展成员不被视为成员约束解决方案的一部分,因此您很不走运。对于涉及一种以上类型的约束(例如 (+) 上的约束),您可以通过使用第二种类型来解决此问题,但对于 GenericZero 上的约束,没有好的解决方法。

【讨论】:

呃!谢谢。这在任何地方都有记录吗? 我不知道官方文档中明确涵盖了它。但见例如***.com/questions/3681142/… 用于确认(也可***.com/questions/25346246/…)。另外,请参阅fslang.uservoice.com/forums/245727-f-language/suggestions/… 了解相关的用户语音项。 请参阅visualfsharp.codeplex.com/workitem/2,了解为什么将其添加到编译器中并非易事。

以上是关于如何将 LanguagePrimitives.GenericZero / get_Zero 添加到 System.String?的主要内容,如果未能解决你的问题,请参考以下文章

如何将Ios文件上传到

Qt如何将文字变成图片?

如何将Bitmap保存为本地图片文件?

在MATLAB中如何将图导出

ASP如何将SQLSERVER数据导出到DBF(VF)

如何将CSV格式转换成JSON格式