使用F#引号检测代码更改

本文关键字:代码 检测 使用 | 更新日期: 2023-09-27 18:00:34

我需要缓存从同一基类继承的几个不同类所进行的大量计算的结果。我正在按子类名称进行运行时缓存。现在,我需要将结果存储在磁盘/DB上,以避免在重新启动应用程序时进行长时间的重新计算,但如果我更改Calculate()中的代码,则需要使缓存无效。

type BaseCalculator() =
    let output = ConcurrentDictionary<string, Series<DateTime, float>>() // has public getter, the output is cached by the caller of Calculate() method
    abstract Calculate: unit->unit
type FirstCalculator() =
    inherit BaseCalculator()
    override this.Calculate() = ... do heavy work here ...

从这个问题和答案中,我了解到我可以在我的计算方法中使用[<ReflectedDefinition>]。但我以前从来没有处理过报价。

问题是:

  • 我可以使用引号的哈希码来唯一标识方法的主体吗?或者引号中有一些guid或时间戳吗
  • [<ReflectedDefinition>]可以应用于抽象方法吗?如果我重写C#中的方法,但从F#runner调用该方法,它会起作用吗?(我使用反射来加载文件夹中所有dll中基类的所有实现)
  • 有没有其他简单可靠的方法可以在没有报价的情况下自动检测程序集中的代码更改?使用程序集文件(dll)的上次修改时间可以工作,但程序集中的任何更改都将使程序集中的所有计算器无效。如果我将stableWIP计算器分离成单独的程序集,这可能会起作用,但更细粒度是首选

使用F#引号检测代码更改

我可以使用引号的哈希码来唯一标识方法的主体吗?或者引号中有一些guid或时间戳吗?

我认为这会奏效。看看FsPickler(MBrace使用它来序列化报价)。我想它也可以给你一个混乱的报价。但请记住,代码的其他部分可能会发生更改(例如,另一种类型中的另一个方法)。

ReflectedDefinition可以应用于抽象方法吗?如果我重写C#中的方法,但从F#runner调用该方法,它会起作用吗?

不,该属性仅适用于使用F#编译器编译的F#方法。

有没有其他简单可靠的方法可以在没有报价的情况下自动检测程序集中的代码更改?

我不确定。您可以使用GetMethodBody方法和.NET反射来获取IL,但这只提供了直接体,不包括lambda函数,因此在其他地方发生的更改将不容易检测到。

一种可能更有效的完全不同的方法是将FSX文件中的计算保持为纯文本,并使用F#编译器服务动态编译。然后,您可以对单个FSX文件的源进行散列(基于每次计算)。

我发现Mono.Cecil非常容易使用,使用它我可以散列一个类型的所有成员体,包括基类型。

这个SO问题作为一个起点非常有用。