如何识别匿名函数

发布于 2024-10-05 19:30:40 字数 1755 浏览 5 评论 0 原文

我有一个类,它创建一个 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 变量作为匿名函数,一切都会工作,但实际上添加和删除永远不会在同一范围内。

I have a class that creates a List<Action<int>> and holds on to them until a later time. This class can add and remove delegates from this list. This works well as long as people don't get too fancy. To combat anonymous function (which can't be removed) I check against the target of the delegate being null. If its null I throw an exception. The problem comes in when there is an anonymous delegate that contains a function. This has a target, but is just as unremovable. The simplified code below illustrates my issues

 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);
    }
}

I need some way to identify

   p => { fake.Test(p); counter++; }

is an anonymous function so I can throw if someone tries it. Thanks for any help

EDIT: I should note that I could use an Action<int> variable for the anonymous function and everything would work, but the Add and Remove are never in the same scope in practice.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

无力看清 2024-10-12 19:31:17

我知道的老问题,但我认为这将是检查方法是否匿名的当前(和未来)证明的方法:

bool isAnonymous = !System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(del.Method.Name);

如果在编译时使用,匿名方法的运行时名称必须是无效的,以确保它没有不冲突。

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:

bool isAnonymous = !System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(del.Method.Name);

The runtime name of the anonymous method would have to be invalid if used at compilation time to ensure that it didn't clash.

醉生梦死 2024-10-12 19:31:10

正如 jonnii 在评论中建议的那样,实现它的另一种方法是使用字典:

public class MyDelegateContainer 
{ 
    Dictionary<string, Action<int>> m_Container =
        new Dictionary<string, Action<int>>(); 

    public void Add(string key, Action<int> del) 
    { 
        m_Container.Add(key, del);
    } 

    public bool Remove(string key) 
    { 
        return m_Container.Remove(key); 
    } 
}

然后,您只需知道用于添加它的名称,就可以轻松地在代码中的任意点删除已知委托:

    container.Add("fake.Test", fake.Test);
    removed = container.Remove("fake.Test");
    Debug.Assert(removed);   
    container.Add("anon", p => { fake.Test(p); counter++; });
    removed = container.Remove("anon"); // works!
    Debug.Assert(removed);   

As jonnii suggested in a comment, another way you could implement it is with a dictionary:

public class MyDelegateContainer 
{ 
    Dictionary<string, Action<int>> m_Container =
        new Dictionary<string, Action<int>>(); 

    public void Add(string key, Action<int> del) 
    { 
        m_Container.Add(key, del);
    } 

    public bool Remove(string key) 
    { 
        return m_Container.Remove(key); 
    } 
}

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:

    container.Add("fake.Test", fake.Test);
    removed = container.Remove("fake.Test");
    Debug.Assert(removed);   
    container.Add("anon", p => { fake.Test(p); counter++; });
    removed = container.Remove("anon"); // works!
    Debug.Assert(removed);   
纵山崖 2024-10-12 19:31:05

当然,您可以删除匿名方法,您只需要拥有对同一匿名方法的引用即可。

var myAnonymousMethod = p => { fake.Test(p); counter++; };
container.Add(myAnonymousMethod);
removed = container.Remove(myAnonymousMethod);

Of course you can remove an anonymous method, you just need to have a reference to the same anonymous method.

var myAnonymousMethod = p => { fake.Test(p); counter++; };
container.Add(myAnonymousMethod);
removed = container.Remove(myAnonymousMethod);
迷离° 2024-10-12 19:31:01

我会使用内省来检查方法的名称。

匿名方法通常具有非常可预测的名称。 (我不记得确切的格式,但运行一些测试,它应该是显而易见的)。

缺点是,如果有人创建了一个非匿名方法,但决定将其命名为 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.

给我一枪 2024-10-12 19:30:57

在您的示例中,调用者负责删除处理程序。因此,如果调用者不想删除处理程序,则无论处理程序是否是匿名委托/lambda,它都不会被删除。

我的建议是将委托容器更改为如下所示:

public class MyDelegateContainer
{
    List<Action<int>> m_Container = new List<Action<int>>();

    public Action Add(Action<int> del)
    {
        m_Container.Add(del);

        return new Action(() =>
        {
            m_Container.Remove(del);
        });
    }
}

调用者仍然负责删除处理程序,但它不是再次将处理程序传递给容器,而是接收一个“令牌”,可以保存该令牌并稍后使用它来删除处理程序处理程序。

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:

public class MyDelegateContainer
{
    List<Action<int>> m_Container = new List<Action<int>>();

    public Action Add(Action<int> del)
    {
        m_Container.Add(del);

        return new Action(() =>
        {
            m_Container.Remove(del);
        });
    }
}

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.

浅浅 2024-10-12 19:30:52

没有办法可靠地确定函数是否是“匿名”的,因为所有函数都有 CLR 的名称。它仅在生成它的语言中是匿名的,并且依赖于编译器。您也许能够确定 Microsoft 当前的 C# 编译器使用的算法,只是让它停止在 C# 5 或 Mono 上工作。

由于您希望防止您类型的用户编写错误使用它的代码,因此您只需在某个点抛出异常,这将使他们的程序崩溃。我要做的是在找不到目标委托时在 Remove 函数中抛出异常。那时,您的用户仍然会遇到崩溃,修复它的唯一方法是以某种可移动的方式编写委托。

作为额外的好处,您将发现有人尝试两次删除委托或从未添加过的错误。代码如下所示:

public bool Remove(Action<int> del) 
{ 
    if (m_Container.Contains(del)) 
    { 
        m_Container.Remove(del); 
        return true; 
    } 

    throw new ArgumentException("Attempt to remove nonexistent delegate");
} 

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:

public bool Remove(Action<int> del) 
{ 
    if (m_Container.Contains(del)) 
    { 
        m_Container.Remove(del); 
        return true; 
    } 

    throw new ArgumentException("Attempt to remove nonexistent delegate");
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文