実行時にCompiledNameが付いた型の元の名前を取得する
正確には実行時じゃないけど解決したのでメモ
元ネタ: http://ja.stackoverflow.com/questions/6177/compilednameが付いた型のf-での名前を取得したい
問題
F#では、CompiledNameAttribute
を付けた型は、リフレクションで元の名前を取得できません。
[<CompiledName("Piyo")>] type Hoge() = class end
というクラスを定義し、リフレクションで型名を取得しようとすると、
> typeof<Hoge>.Name;; val it : string = "Piyo"
"Piyo"
という文字列が返ります。
ソースコード上では"Hoge"
という名前でしかアクセスできないのに、取得できないのは何かと不便ですね?
コンパイル前の名前が格納されている場所
アセンブリに、リソースとしてFSharpSignatureData.{AssemblyName}
という名前で埋め込まれています。
typeof<Piyo>.Assembly.GetManifestResourceStream("FSharpSignatureData.Library1")
というコードで取得できます。
詳しいフォーマットは分かりませんが、取得できたバイナリを覗いてみると確かに"Hoge"
という文字列が入っています。
FSharp.Compiler.Service を使うといい
これらのページを参考にすると、
http://fsharp.github.io/FSharp.Compiler.Service/ja/project.html https://github.com/fsharp/FSharp.Compiler.Service/issues/187
こんな感じのコードで取得できるみたいです。
module CompilerServiceStudy [<CompiledName("Piyo")>] type Hoge() = class end open System.IO let base1 = Path.GetTempFileName() let fileName1 = Path.ChangeExtension(base1, ".fs") let projFileName = Path.ChangeExtension(base1, ".fsproj") let dllName = Path.ChangeExtension(base1, ".dll") open Microsoft.FSharp.Compiler.SourceCodeServices let asm = typeof<Hoge>.Assembly let checker = FSharpChecker.Create() let projectOptions = let sysLib name = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) + @"\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\" + (name + ".dll") let fsCore4300() = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFilesX86) + @"\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0\FSharp.Core.dll" checker.GetProjectOptionsFromCommandLineArgs (projFileName, [| yield "--simpleresolution" yield "--noframework" yield "--debug:full" yield "--define:DEBUG" yield "--optimize-" yield "--out:" + dllName yield "--warn:3" yield "--fullpaths" yield "--flaterrors" yield "--target:library" yield fileName1 let references = [ sysLib "mscorlib" sysLib "System" sysLib "System.Core" fsCore4300() asm.Location ] for r in references do yield "-r:" + r |]) let wholeProjectResults = checker.ParseAndCheckProject(projectOptions) |> Async.RunSynchronously let refAssemblies = wholeProjectResults.ProjectContext.GetReferencedAssemblies() let thisAssembly = refAssemblies |> List.find (fun a -> a.SimpleName = asm.GetName().Name) let thisModule = thisAssembly.Contents.Entities |> Seq.find (fun e -> e.DisplayName = "CompilerServiceStudy") let piyoClass = thisModule.NestedEntities |> Seq.find (fun e -> e.CompiledName = "Piyo") let hogeName = piyoClass.DisplayName
> CompilerServiceStudy.hogeName;; val it : string = "Hoge"
無事"Hoge"
が取得できました。ただし内部でコンパイルしているため、初めの1回はすごく遅いです。