リフレクション 逆引き (F#)
F#でリフレクションを利用するときに、やり方を忘れていることがよくあるため、ここを備忘録にしたいと思います。
今後適宜追加していこうと思います。
Type取得
let t = typeof<int>
TypeDefinition取得
let t = typedefof<List<_>>
TypeDefinitionからType作成
let t = typedefof<List<_>>.MakeGenericType(typeof<int>) // List<int>
TypeからTypeDefinition取得
typeof<List<int>>.GetGenericTypeDefinition() = typedefof<List<_>> // true
Cast可能かどうか
typeof<IEnumerable<int>>.IsAssignableFrom(typeof<List<int>>) // true
static classかどうか
let t = typeof<Enumerable> t.IsAbstract && t.IsSealed // true
ロードされているAssembly全て取得
let assemblies = System.AppDomain.CurrentDomain.GetAssemblies()
オーバーロードかつジェネリックな関数を取得
現状GetMethods()して絞り込む方法しかなさそうです。
下記はSystem.Linq.Enumerable.Selectの一つを取得しています。
let selectMethod = typeof<Enumerable>.GetMethods(BindingFlags.Static ||| BindingFlags.Public) |> Array.find (fun x -> let parameters = x.GetParameters() |> Array.map (fun x -> x.ParameterType.GetGenericTypeDefinition()) |> Array.toList x.Name = "Select" && parameters = [ typedefof<IEnumerable<_>>; typedefof<Func<_, _>> ])
拡張メソッドかどうか
let method = typeof<Enumerable>.GetMethod("All") method.IsDefined(typeof<ExtensionAttribute>, true) // true
Cast可能な型一覧取得
ベースクラスはBaseTypeで、インターフェースはGetInterfaces()で取得します。
インターフェースの継承関係はツリー構造になるため、flatTreeを用意しています。
let assignableTypes (type_ : Type) = let flatTree getChildren root = [ root ] |> List.unfold (function | [] -> None | h::t -> Some (h, t @ getChildren h)) type_ |> List.unfold (Option.ofObj >> Option.map (fun x -> x, x.BaseType)) |> List.collect (flatTree (fun x -> x.GetInterfaces() |> Array.toList)) |> List.distinct
UnionCaseの名前取得
let caseName (x : obj) = FSharpValue.GetUnionFields(x, x.GetType()) |> fst |> (fun x -> x.Name)