#net #рефлексия #f#
Заинтересовал вопрос как получить информацию о функциях которые содержит модуль.
Хочу получить что-то вроде того, что выдает FSI.
Например для следующего модуля
module Test =
let ign _ = ()
let getNowDateTime() = System.DateTime.Now
let getNumbers count = [1..count]
FSI выводит такую информацию
module Test = begin
val ign : 'a -> unit
val getNowDateTime : unit -> System.DateTime
val getNumbers : count:int -> int list
end
Пробовал достать данные через рефлексию следующим образом
let getInfoAboutModule (t : Type) =
let genericToString (t : Type) =
match t.GenericTypeArguments with
| [| |] -> t.FullName
| x ->
x
|> Seq.map (fun x -> x.Name)
|> String.concat ","
|> sprintf "%s<%s>" t.Name
let getInfo (mi:MethodInfo) =
let parameter =
let sb = System.Text.StringBuilder()
for x in mi.GetParameters() do
x.ParameterType
|> genericToString
|> sprintf "%s : %s ->" x.Name
|> sb.Append
|> ignore
sb.ToString()
|> fun str -> if str |> System.String.IsNullOrEmpty then "unit -> " else str
sprintf "%s : %s %s" mi.Name parameter (genericToString mi.ReturnType)
t.GetMethods(BindingFlags.Public ||| BindingFlags.Static)
|> Seq.map getInfo
для модуля Test результат будет следующим
ign : _arg1 : -> System.Void
getNowDateTime : unit -> System.DateTime
getNumbers : count : System.Int32 -> FSharpList`1
MCVE (ideone)
но, естественно, таким образом получаю названия далекие от F#-ных.
P.S. Добавлять какие-либо атрибуты к модулю нельзя, т.к. нужно оставить возможность
извлекать данные из модулей из других сборок.
Ответы
Ответ 1
Можно воспользоваться FSharp.Compiler.Service для анализа исходных текстов модулей. Если модуль скомпилирован и его исходных текстов нет, то сомневаюсь, что можно добиться лучшего результата, чем ваш. На основе этого примера: open System open System.IO open Microsoft.FSharp.Compiler.SourceCodeServices let text = """ module Test = let ign _ = () let getNowDateTime() = System.DateTime.Now let getNumbers count = [1..count] """ let checker = FSharpChecker.Create() // из примера let parseAndTypeCheckSingleFile input = let file = "test.fsx" let projOptions = checker.GetProjectOptionsFromScript(file, input) |> Async.RunSynchronously let parseFileResults, checkFileResults = checker.ParseAndCheckFileInProject(file, 0, input, projOptions) |> Async.RunSynchronously // Wait until type checking succeeds (or 100 attempts) match checkFileResults with | FSharpCheckFileAnswer.Succeeded(res) -> parseFileResults, res | res -> failwithf "Parsing did not finish... (%A)" res // печать информации о члене модуля let printInfo (fn:FSharpMemberOrFunctionOrValue) = // строка с информацией о типе let rec getTypeString (t:FSharpType) = if t.HasTypeDefinition then let prms = [ for x in t.GenericArguments -> (getTypeString x) ] in t.TypeDefinition.DisplayName + " " + String.Join(" ", prms) else t.ToString() printf "%s: " fn.DisplayName for group in fn.CurriedParameterGroups do for prm in group do match prm.Name with | None -> () | Some name -> printf "%s:" name printf "%s -> " (getTypeString prm.Type) printf "%s\n" (getTypeString fn.ReturnParameter.Type) [] let main argv = let parseFileResults, checkFileResults = parseAndTypeCheckSingleFile text let m = checkFileResults.PartialAssemblySignature.Entities.[0] let fns = m.NestedEntities.[0].MembersFunctionsAndValues for x in fns do printInfo x 0 Результат: ign: type 'a -> unit getNowDateTime: unit -> DateTime getNumbers: count:int -> list int Либо можно поизучать исходники непосредственно FSI. Если возникает ошибка при парсинге, нужно скопировать файлы FSharp.Core.optdata и FSharp.Core.sigdata в каталог с программой. Файлы можно найти в %PROFRAMFILES%\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0\.
Комментариев нет:
Отправить комментарий