読者です 読者をやめる 読者になる 読者になる

リフレクション 逆引き (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