说,我有
member this.Test (x: 'a) = printfn "generic"
1
member this.Test (x: Object) = printfn "non generic"
2
Run Code Online (Sandbox Code Playgroud)
如果我在C#中调用它
var a = test.Test(3); // calls generic version
var b = test.Test((object)3); // calls non generic version
var c = test.Test<object>(3); // calls generic version
Run Code Online (Sandbox Code Playgroud)
但是,在F#中
let d = test.Test(3); // calls non generic version
let e = test.Test<int>(3); // calls generic version
Run Code Online (Sandbox Code Playgroud)
所以我必须添加类型注释才能获得正确的重载方法.这是真的?如果是这样,那么为什么F#不能自动解析,因为已经推断出了参数类型?(无论如何,F#的重载决策顺序是什么?总是Object比它的继承类更受青睐?)
如果一个方法同时具有两个重载,其中一个将参数作为Object类型而另一个是泛型的并且两者都返回相同的类型,则有点危险.(就像在这个例子中,或者Assert.AreEqual在单元测试中),因为我们很可能在没有通知的情况下得到错误的重载(不会出现任何编译器错误).这不是问题吗?
更新:
有人可以解释一下
为什么F#解析Assert.AreEqual(3, 5)为Assert.AreEqual(Object a, Object b)但不是Assert.AreEqual<T>(T a, T b)
但F#解决Array.BinarySearch([|2;3|], 2)了BinarySearch<T>(T[]array, T value)但不是BinarySearch(Array array, Object value)
F#方法的重载决策不如C#聪明吗?
我不认为这是真的.方法重载使类型推断更加困难.F#有合理的权衡,使方法重载可用,类型推断尽可能强大.
将值传递给函数/方法时,F#编译器会自动将其向上转换为适当的类型.这在许多情况下都很方便,但有时也会令人困惑.
在您的示例中,3是否会进行obj输入.两种方法都适用,但选择了更简单(非通用)的方法.
第14.4节方法应用程序规范中的解决方案非常清楚地指定了重载规则:
1)优选使用不限制使用用户引入的泛型类型注释的候选者等于另一种类型.
2)首选不使用ParamArray转换的候选者.如果两个候选者都使用paramArray转换类型pty1和pty2,并且pty1可行地包含pty2,则更喜欢第二个; 也就是说,使用具有更精确类型的候选者.
3)喜欢没有ImplicitlyReturnedFormalArgs的候选人.
4)首选没有ImplicitlySuppliedFormalArgs的候选人.
5)如果两个候选人有未命名的实际参数类型ty11 ... ty1n和ty21 ... ty2n,并且每个ty1i要么
一个.可行地包含ty2i,或
湾 ty2i是System.Func类型,ty1i是其他一些委托类型,然后更喜欢第二个候选.也就是说,更喜欢具有更具体的实际参数类型的任何候选者,并且认为任何System.Func类型比任何其他委托类型更具体.
6)优先选择非候选人的候选人.
7)要在两个扩展成员之间进行选择,请选择最近使用open的成员.
8)选择非通用的候选者而不是通用的候选者 - 即,更喜欢具有空的ActualArgTypes的候选者.
我认为用户有责任创建明确的重载方法.您始终可以查看推断类型以查看是否正确执行了这些类型.例如,您的修改版本没有歧义:
type T() =
member this.Test (x: 'a) = printfn "generic"; 1
member this.Test (x: System.ValueType) = printfn "non-generic"; 2
let t = T()
let d = t.Test(3) // calls non-generic version
let e = t.Test(test) // call generic version
Run Code Online (Sandbox Code Playgroud)
更新:
它归结为核心概念,协方差.F#不支持数组,列表,函数等的协方差.确保类型安全通常是一件好事(参见本例).
因此,很容易解释为什么Array.BinarySearch([|2;3|], 2)要解决BinarySearch<T>(T[] array, T value).这是函数参数的另一个例子
T.Test((fun () -> 2), 2)
Run Code Online (Sandbox Code Playgroud)
决心
T.Test(f: unit -> 'a, v: 'a)
Run Code Online (Sandbox Code Playgroud)
但不是
T.Test(f: unit -> obj, v: obj)
Run Code Online (Sandbox Code Playgroud)