如何识别匿名函数
我有一个类,它创建一个 List
并保留它们直到稍后。此类可以在此列表中添加和删除委托。只要人们不太花哨,这种方法就很有效。为了对抗匿名函数(无法删除),我检查委托的目标是否为空。如果它为空,我会抛出异常。当存在包含函数的匿名委托时,就会出现问题。它有一个目标,但同样不可移动。下面的简化代码说明了我的问题,
public class MyDelegateContainer
{
List<Action<int>> m_Container = new List<Action<int>>();
public void Add(Action<int> del)
{
if (del.Target == null)
{
throw new Exception("No static handlers");
}
m_Container.Add(del);
}
public bool Remove(Action<int> del)
{
if (m_Container.Contains(del))
{
m_Container.Remove(del);
return true;
}
return false;
}
}
public class MyFakeActionClass
{
public void Test(int temp) { }
}
class Program
{
static void Main(string[] args)
{
bool removed = false;
int counter = 0;
MyDelegateContainer container = new MyDelegateContainer();
MyFakeActionClass fake = new MyFakeActionClass();
//container.Add(p => { }); //Throws, this is what I want to happen
container.Add(fake.Test); //Works, this is the use case
removed = container.Remove(fake.Test); //Works, this is the use case
Debug.Assert(removed);
container.Add(p => { fake.Test(p); counter++; }); //Works but I would like it not to
removed = container.Remove(p => { fake.Test(p); counter++; }); //doesn't work
Debug.Assert(removed);
}
}
我需要某种方法来识别
p => { fake.Test(p); counter++; }
它是一个匿名函数,这样如果有人尝试它我就可以抛出它。感谢您的任何帮助
编辑:我应该注意,我可以使用 Action
变量作为匿名函数,一切都会工作,但实际上添加和删除永远不会在同一范围内。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我知道的老问题,但我认为这将是检查方法是否匿名的当前(和未来)证明的方法:
如果在编译时使用,匿名方法的运行时名称必须是无效的,以确保它没有不冲突。
Old question I know but I would think that this would be a current (and future) proofed way of checking if a method is anonymous:
The runtime name of the anonymous method would have to be invalid if used at compilation time to ensure that it didn't clash.
正如 jonnii 在评论中建议的那样,实现它的另一种方法是使用字典:
然后,您只需知道用于添加它的名称,就可以轻松地在代码中的任意点删除已知委托:
As jonnii suggested in a comment, another way you could implement it is with a dictionary:
Then you could easily remove a known delegate at some arbitrary point in your code just by knowing what name was used to add it:
当然,您可以删除匿名方法,您只需要拥有对同一匿名方法的引用即可。
Of course you can remove an anonymous method, you just need to have a reference to the same anonymous method.
我会使用内省来检查方法的名称。
匿名方法通常具有非常可预测的名称。 (我不记得确切的格式,但运行一些测试,它应该是显而易见的)。
缺点是,如果有人创建了一个非匿名方法,但决定将其命名为
anonMethod123
(或任何格式......),它将被错误地拒绝。I would use introspection to check the names of the methods.
Anonymous methods typically have very predictable names. (I don't remember the exact format, but run some tests, and it should be obvious).
The drawback would be that if anyone created a non-anonymous method, but decided to name it
anonMethod123
(or whatever the format is...) It would be falsely rejected.在您的示例中,调用者负责删除处理程序。因此,如果调用者不想删除处理程序,则无论处理程序是否是匿名委托/lambda,它都不会被删除。
我的建议是将委托容器更改为如下所示:
调用者仍然负责删除处理程序,但它不是再次将处理程序传递给容器,而是接收一个“令牌”,可以保存该令牌并稍后使用它来删除处理程序处理程序。
In your example, the caller is responsible from removing the handler. So, if the caller doesn't want to remove the handler, it won't get removed, no matter if the handler is an anonymous delegate/lambda or not.
My suggestion is to change the delegate container to something like this:
The caller is still responsible for removing the handler, but instead of passing the handler again to the container, it receives a "token" that it can save and use later to remove the handler.
没有办法可靠地确定函数是否是“匿名”的,因为所有函数都有 CLR 的名称。它仅在生成它的语言中是匿名的,并且依赖于编译器。您也许能够确定 Microsoft 当前的 C# 编译器使用的算法,只是让它停止在 C# 5 或 Mono 上工作。
由于您希望防止您类型的用户编写错误使用它的代码,因此您只需在某个点抛出异常,这将使他们的程序崩溃。我要做的是在找不到目标委托时在
Remove
函数中抛出异常。那时,您的用户仍然会遇到崩溃,修复它的唯一方法是以某种可移动的方式编写委托。作为额外的好处,您将发现有人尝试两次删除委托或从未添加过的错误。代码如下所示:
There is no way to reliably determine whether a function is "anonymous" because all functions have names to the CLR. It's only anonymous within the language that generates it, and that's compiler-dependent. You may be able to determine the algorithm used by Microsoft's current C# compiler, only to have it stop working on C# 5 or Mono.
Since you want to prevent users of your type from writing code that uses it wrong, you just need to throw an exception at some point that will make their program crash. What I would do is throw the exception in the
Remove
function when the target delegate isn't found. At that point your users will still get a crash and the only way to fix it is to write the delegate in some way that it's removable.As an added bonus, you will catch bugs where somebody tries to remove delegates twice or that were never added in the first place. The code would look like this: