Generics ToString()与字符串运算符的类型推断
注意:这个问题与我的有点相关,但实际上它从不同的角度触及了这个问题 考虑以下代码段:Generics ToString()与字符串运算符的类型推断,generics,f#,type-inference,Generics,F#,Type Inference,注意:这个问题与我的有点相关,但实际上它从不同的角度触及了这个问题 考虑以下代码段: let toStr a = a.ToString() let strOp a = string a let intToStr = 5 |> toStr let floatToStr = 5.0 |> toStr let intStrOp = 5 |> strOp let floatStrOp = 5.0 |> strOp //type inference error 虽然strOp
let toStr a = a.ToString()
let strOp a = string a
let intToStr = 5 |> toStr
let floatToStr = 5.0 |> toStr
let intStrOp = 5 |> strOp
let floatStrOp = 5.0 |> strOp //type inference error
虽然strOp
函数使用了看起来更优雅的解决方案,并且能够将单位值转换为字符串,但它似乎不是真正的泛型,因为它的类型在第一次使用时受到限制(即使推断的类型是obj->string
,而不是'a->string
)
为什么字符串运算符不能以这样一种通用的方式工作?或者我做错了什么?区别在于
string
使用静态成员约束(请参阅),而ToString
是任何对象上可用的普通方法,因此编译器将ToString
调用视为通用代码,不以任何方式约束实例类型
静态约束和(非静态)泛型在(否则不受约束的)let
-绑定函数中使用时以不同的方式工作:
- 对于泛型代码,编译器传播泛型性,并使您编写的
绑定函数也具有泛型性let
- 对于静态成员约束,编译器根据第一次使用专门化代码。如注释中所述,您可以通过使用
来避免这种情况,它允许基于静态成员的泛型以与顺序泛型代码相同的方式传播内联
我认为
string
函数使用静态解析的类型约束的唯一原因是它允许它专门化为对基本类型的普通ToString
调用,但是仍然以自定义方式处理对象的null
值-tostrnull
抛出异常,但strOp null
返回空字符串 让内联strOp a=string a
这是实现更高级多态性的著名技巧吗?如果我的问题是关于这一点,那么ToString示例为什么有效?我不明白其中的区别。因为toStr
是一个真正的泛型函数,而strOp
用静态解析的类型约束实例化了一个内联函数,但它本身不是内联的,因此不具有泛型性。这可能有助于解释细节:我不认为处理null是string
具有静态约束的原因。如果您看一下,这些类型专门化看起来相当面向性能,即生成 <代码>调用而不是已知基元类型的callvirt
。空检查本身在via-boxing中执行得非常好,没有静态约束。