开发者

How to identify an anonymous function

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.


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.


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


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.


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


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜