私はF#を勉強していますが、柔軟な型の目的がわかりません。もっと良いのは、これを書くことの違いを理解できないことです。
set TextOfControl (c : Control) s = c.Text <- s
そしてこれを書く:
set TextOfControl (c : 'T when 'T :> Control) s = c.Text <- s
クラスControl
はどこですか。System.Windows.Forms.Control
私はF#を勉強していますが、柔軟な型の目的がわかりません。もっと良いのは、これを書くことの違いを理解できないことです。
set TextOfControl (c : Control) s = c.Text <- s
そしてこれを書く:
set TextOfControl (c : 'T when 'T :> Control) s = c.Text <- s
クラスControl
はどこですか。System.Windows.Forms.Control
あなたの例に違いはありません。戻り値の型が制約されている場合、違いが見え始めます。
let setText (c: Control) s = c.Text <- s; c
let setTextGeneric (c: #Control) s = c.Text <- s; c
let c = setText (TreeView()) "" // return a Control object
let tv = setTextGeneric (TreeView()) "" // return a TreeView object
#Control
のショートカットであることに注意してください'T when 'T :> Control
。
サブタイプのジェネリック関数を作成するには、型制約が重要です。
例えば、
let create (f: _ -> Control) = f()
let c = create (fun () -> Control()) // works
let tv = create (fun () -> TreeView()) // fails
対。
let create (f: _ -> #Control) = f()
let c = create (fun () -> Control()) // works
let tv = create (fun () -> TreeView()) // works
値を F# 関数に引数として直接渡す場合、コンパイラは自動的に値をアップキャストします (したがって、関数が を受け取る場合はControl
、それに値を渡すことができTextBox
ます)。そのため、パラメータの型として柔軟な型を使用する場合、大きな違いはありません。
ただし、関数が list などを取る場合は違いがあります'T list
。
// Takes a list of any subtype of object (using flexible type)
let test1<'T when 'T :> obj> (items:'T list) =
items |> List.iter (printfn "%A")
// Takse a list, which has to be _exactly_ a list of objects
let test2 (items:obj list) =
items |> List.iter (printfn "%A")
// Create a list of System.Random values (System.Random list)
let l = [new System.Random()]
test1 l // This works because System.Random is subtype of obj
test2 l // This does not work, because the argument has wrong type!