Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Получение информации о функциях в модуле

#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\.

Комментариев нет:

Отправить комментарий