Самый быстрый способ проверить, захватывает ли Action / (FSharpFunc) внешнее состояние - PullRequest
0 голосов
/ 06 октября 2019

Какой самый быстрый способ проверить, захватывает ли действие или FSharpFunc внешнее состояние?

Пример:

[<EntryPoint>]
let main(args: string[]) =
    let count = 1

    // func captures outer variable count
    let capturesState = fun args -> count |> ignore
    capturesState args
    0

1 Ответ

0 голосов
/ 18 октября 2019

В настоящее время нет способа сделать это без отражения. Это решение, которое я придумал, и оно работает довольно хорошо. До сих пор у него не было проблем с этим, но имейте в виду, что это не пуленепробиваемое и, возможно, есть какой-то край, о котором я не думал.

Как это работает?

Рассмотрим следующую функцию 'a', которая фиксирует состояние.

let o = "o" // non static!

// function captures outer variable 'o'
let a = (fun () ->
    printf "%s" o
)

Функция, которую мы связываем с 'a', превращается в класс, сгенерированный компилятором, который содержит фактический код в методе с именем Invoke. ,Если функция не захватывает состояние, то у нее есть конструктор без параметров. Ему не нужно ничего, что не передано в invoke. НО, если он захватывает состояние, класс будет иметь конструктор, принимающий <1 параметров. Это то, что мы будем использовать для определения, фиксирует ли функция состояние. </p>

Кроме того, поскольку производительность является проблемой, мы кешируем результат анализа.

Вот как выглядит код:

module FunctionAnalysis =
    open System
    open System.Reflection
    open System.Collections.Concurrent

    let internal cache = ConcurrentDictionary<Type, bool>()

    let private flags =
        BindingFlags.Instance |||
        BindingFlags.NonPublic |||
        BindingFlags.Public

    let capturesState (func : 'a) : bool =
        let type' = func.GetType()

        let hasValue, value = cache.TryGetValue type'

        match hasValue with
        | true -> value
        | false ->
            let capturesState =
                type'.GetConstructors(flags)
                |> Array.map (fun info -> info.GetParameters().Length)
                |> Array.exists (fun parameterLength -> parameterLength > 0)

            cache.AddOrUpdate(type', capturesState, (fun identifier lastValue -> capturesState))
...