7

言う、私は持っています

member this.Test (x: 'a) = printfn "generic"
                           1
member this.Test (x: Object) = printfn "non generic"
                               2

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

ただし、F#では

let d = test.Test(3);  // calls non generic version
let e = test.Test<int>(3); // calls generic version

したがって、正しいオーバーロードされたメソッドを取得するために、型アノテーションを追加する必要があります。これは本当ですか?もしそうなら、引数の型がすでに推測されているのに、なぜF#が自動的に正しく解決されないのですか?Object(とにかく、F#のオーバーロード解決の順序は何ですか?継承されたクラスよりも常に優先されますか?)

メソッドに両方のオーバーロードがあり、一方が型として引数を取り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)

4

1 に答える 1

9

F#メソッドのオーバーロード解決はC#ほど賢くないですか?

私はそれが本当だとは思わない。メソッドのオーバーロードは、型推論をはるかに困難にします。F#には、メソッドのオーバーロードを使用可能にし、型推論を可能な限り強力にするための合理的なトレードオフがあります。

関数/メソッドに値を渡すと、F#コンパイラは自動的にその値を適切な型にアップキャストします。これは多くの状況で便利ですが、混乱することもあります。

あなたの例で3は、はタイプにアップキャストされていobjます。どちらの方法も適用できますが、より単純な(一般的ではない)方法が選択されます。

セクション14.4仕様のメソッドアプリケーション解決は、オーバーロードルールを非常に明確に指定しています。

1)ユーザーが導入したジェネリック型アノテーションの使用が別の型と同じになるように使用を制限しない候補を優先します。

2)ParamArray変換を使用しない候補を優先します。2つの候補が両方ともタイプpty1およびpty2でParamArray変換を使用し、pty1がpty2を適切に包含している場合は、2番目を優先します。つまり、より正確なタイプの候補を使用します。

3)ImplicitlyReturnedFormalArgsを持たない候補者を優先します。

4)ImplicitlySuppliedFormalArgsを持たない候補者を優先します。

5)2つの候補に名前のない実際の引数タイプty11...ty1nおよびty21...ty2nがあり、各ty1iがいずれかである場合

a。実行可能にty2iを包含する、または

b。ty2iはSystem.Funcタイプであり、ty1iは他のデリゲートタイプであり、2番目の候補を優先します。つまり、より具体的な実際の引数タイプを持つ候補を優先し、System.Funcタイプは他のデリゲートタイプよりも具体的であると見なします。

6)拡張メンバーである候補者よりも拡張メンバーではない候補者を優先します。

7)2つの拡張メンバーから選択するには、openを最近使用した結果のメンバーを選択します。

8)一般的な候補よりも一般的でない候補を優先します。つまり、ActualArgTypeが空の候補を優先します。

明確なオーバーロードされたメソッドを作成するのはユーザーの責任だと思います。推論された型をいつでも調べて、正しく実行されているかどうかを確認できます。たとえば、あいまいさのない修正バージョン:

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

アップデート:

それは、コアコンセプトである共分散に帰着します。F#は、配列、リスト、関数などの共分散をサポートしていません。通常、型の安全性を確保することは良いことです(この例を参照)。

Array.BinarySearch([|2;3|], 2)したがって、がに解決される理由を簡単に説明できBinarySearch<T>(T[] array, T value)ます。関数の引数に関する別の例を次に示します。

T.Test((fun () -> 2), 2)

に解決されます

T.Test(f: unit -> 'a, v: 'a)

しかし、

T.Test(f: unit -> obj, v: obj)
于 2013-01-31T10:41:16.433 に答える