4

の形式でいくつかの計算の結果を取得してい'a option when 'a :> IBaseTypeます。から派生した型のツリーがありIBaseType、これがどの特定の型のオプションであるかはよくわかりませんが、重要なことは、それが特定の派生型のオプションであり、基本型ではないということです。だから私はIBaseType optionそれをさらに処理するためにアップキャストしたい。オプションはジェネリック型であるため、(F# で) 直接キャストすることは不可能であり、Option.map 内でキャストする必要があります。複雑なことは何もなく、型推論は期待どおりに機能します... 推論OK 中間のキャストされたオプションも期待どおりに解決されます... 中間推論OK 関数が完了するまで。この時点で、何らかの理由で型推論により、元のオプションは既に type である必要があると判断されましたIBaseType option推論がうまくいかない

中間型は以前に既に解決されていましたが、なぜ の推論された型を再割り当てすることにしたのopですか? 確かにこれは実行時例外につながります。コンパイラ エラーのように見えますが、主なルールはコンパイラにエラーがないことです。

結局、それは本当にばかげているように聞こえます。単純なオプションを単純にアップキャストする方法がわかりません。画像をわかりやすくするために、引数としてprocessResult取りIBaseType optionます。そして、ここに厄介な機能の原因があります:

(fun (x: obj) -> 
    let op = x :?> _ option
    let upcastOp = op |> Option.map (fun y -> y :> IBaseType)
    upcastOp |> processResult)

これに対処する方法はありますか?

4

3 に答える 3

3

そもそもボックス化されたオブジェクトをどのように作成していますか? 最も簡単な解決策は、最初から s をボックス化するのIBaseType optionではなく、 an をボックス化することです。#IBaseType option何らかの理由でそれが実現できない場合は、おそらくリフレクションを使用する必要があります。問題は、このコード ブロックで次のことです。

let op = x :?> _ option
let upcastOp = op |> Option.map (fun y -> y :> IBaseType)

opコンパイラはそれが'a option when 'a :> IBaseTypefor であることを知って'aいますが、コンパイラが実際に何であるかを理解できるようにするもの'a何もありません。これは、この型が関数の最終出力に反映されていないためです。それが作ることができると思うのは、基本型だけです。代わりに、次のようにする必要があります。'aIBaseType

type ResultProcessor =
    static member ProcessResult<'a when 'a :> IBaseType> (op:'a option) =
        let upcastOp = op |> Option.map (fun y -> y :> IBaseType)
        upcastOp |> processResult

fun (x:obj) ->
    let ty = x.GetType()  // must be option<something>
    let [| tyArg |] = ty.GetGenericArguments()
    typeof<ResultProcessor>.GetMethod("ProcessResult").MakeGenericMethod(tyArg).Invoke(null, [|x|])    
于 2012-11-13T18:46:37.817 に答える
1

opここで の型がどのように推測されるのかわかりません。しかし、kvb で提案されているように型をxto に変更できない場合は、本当にリフレクションを使用する必要があると確信しています。IBaseType option

別の反射ベースのソリューション:

let f (x:obj) =
   match x with
   | null -> None  // x is None
   | _ -> match x.GetType().GetProperty("Value") with
          | null -> None  // x is not an option
          | prop ->  
               let v = prop.GetValue( x, null )
               Some (v :?> IBaseType)
于 2012-11-13T20:01:25.397 に答える
1

kvb のソリューションをサポートします。さて、同様のコードに対して行ったいくつかのベンチマークでは、絶対的なパフォーマンスのために、動的な (未知の) メソッド呼び出しを避けることが利点であることがわかりました。どういうわけか、ジェネリック型の新しいインスタンス化を作成する方が高速です。例えば:

[<AbstractClass>]
type BaseResultProcessor() =
    abstract member ProcessResult : obj -> option<IBaseType>

[<Sealed>]
type ResultProcessor<'T when 'T :> IBaseType>() =
    inherit BaseResultProcessor()
    override this.ProcessResult(x: obj) =
        match x :?> option<'T> with
        | Some x -> Some (x :> IBaseType)
        | None -> None

module Example =
    let run (x: obj) =
        let ty = x.GetType()
        let tyArg = ty.GetGenericArguments().[0]
        let p =
            typedefof<ResultProcessor<_>>.MakeGenericType(tyArg)
            |> Activator.CreateInstance :?> BaseResultProcessor
        p.ProcessResult(x)

何が問題なのかというと、次の「直感的な」推論は .NET では無効です。

'T1 :> 'T2
--------------------------
option<'T1> :> option<'T2>

これは型システムの典型であると言えます。型システム全体との相互作用を考慮すると、簡単または直感的に見えるものを正しく実装することは困難または不可能です。

于 2012-11-14T13:23:38.347 に答える