1

我有一个我开始实施的缓存想法:

记忆 function并将返回值与函数签名的哈希一起存储在Velocity中。使用PostSharp,我想检查缓存并返回返回值的重新水化表示,而不是再次调用该函数。我想使用属性来控制这种行为。

不幸的是,这对我组织中的其他开发人员来说可能是危险的,如果他们爱上了性能提升并开始用缓存属性装饰每一个可见的方法,包括一些带有副作用的方法。当记忆库怀疑某个函数可能会导致副作用时,我想退出编译器警告。

使用 CodeDom 或 Reflection 如何判断代码可能会导致副作用?

4

3 回答 3

8

这是一个非常困难的问题,无论是在实践中还是在理论上。我们正在努力考虑如何针对您的场景(记忆化、自动并行化等)预防或隔离副作用,但这很困难,而且我们距离 C# 的可行解决方案还很远。所以,没有承诺。(如果你真的想消除副作用,可以考虑切换到 Haskell。)

不幸的是,即使发生了奇迹并且你找到了一种方法来防止记忆有副作用的方法,你仍然遇到了一些问题。考虑以下:

1)如果你记忆一个本身调用记忆函数的函数怎么办?这是一个很好的情况,对吧?您希望能够编写记忆函数。但是记忆化有一个副作用:它将数据添加到缓存中!所以你马上就有了一个元问题:你想驯服副作用,但只有“坏”的副作用。你想鼓励的“好”,你想阻止的坏,很难把它们区分开来。

2)您将如何处理异常?你能记住一个抛出异常的方法吗?如果是这样,它总是抛出相同的异常,还是每次都抛出一个新的异常?如果是前者,你打算怎么做?如果是后者,现在你有一个记忆函数,它在两个不同的调用上有两个不同的结果,因为抛出了两个不同的异常。异常可以看作是副作用;很难驯服例外。

3) 对于没有副作用但仍然不纯的方法,您将如何处理?假设您有一个 GetCurrentTime() 方法。这没有副作用;调用不会改变任何内容。但这仍然不是记忆的候选者,因为任何两个调用都需要产生不同的结果。您不需要副作用检测器,您需要纯度检测器。

我认为你最好的选择是通过教育和代码审查来解决人类问题,而不是试图解决技术难题。

于 2009-07-28T16:16:01.220 回答
0

简单地说,您不能使用 CodeDom 或 Reflection。

要准确确定一种方法是否会导致副作用,您必须了解它正在采取什么行动。对于 .Net,这意味着打开 IL 并以某种方式对其进行干预。

Reflection 或 CodeDom 都没有为您提供这种能力。

  • CodeDom 是一种将代码生成到应用程序中的方法,它的检查能力非常有限。它本质上仅限于各种解析引擎所理解的语言子集。
  • 反射的优势在于它能够检查元数据,而不是方法体的底层 IL。元数据只能为您提供一组非常有限的信息,说明什么会引起副作用,什么不会引起副作用。
于 2009-07-28T15:30:18.723 回答
0

反射本身不会这样做,因为元数据没有任何此类属性。

CodeDom 可能不够强大,无法检查所有 IL 指令。

因此,您必须使用反射 API 的非常低级的部分,让您获得byte[]包含每个方法的原始 IL 并对其进行分析。所以原则上是可能的,但并不容易。

您必须分析所有指令并观察它们有什么效果,以及这些效果是否会在某些重要范围之外继续存在(例如,它们是否修改了可以通过返回值或out参数泄漏的对象字段,或者他们只是修改了保证在方法之外无法访问的瞬态对象?)。

听起来相当复杂!

于 2009-07-28T15:35:10.633 回答