How to know if an Action is an anonymous method or not?
Asked Answered
L

2

8

This is C# 4.0.

I have a class that stores WeakReferences on some Actions like that:

public class LoremIpsum
{
    private Dictionary<Type, List<WeakReference>> references = new Dictionary<Type, List<WeakReference>>();

    public void KeepReference<T>(Action<T> action)
    {
        if (this.references.ContainsKey(typeof(T)))
        {
            this.references[typeof(T)].Add(new WeakReference(action));
        }
        else
        {
            this.references.Add(typeof(T), new List<WeakReference> { new WeakReference(action) });
        }
    }
}

This class has another method allowing to execute the Actions passed to it later but it's of little importance in this question.

and I consume this class this way:

public class Foobar
{
    public Bar Bar { get; set; }

    public void Foo(LoremIpsum ipsum)
    {
        ipsum.KeepReference<Bar>((b) => { this.Bar = b; });
        ipsum.KeepReference<Bar>(this.Whatever);
    }

    public void Whatever(Bar bar)
    {
        // Do anything, for example...:
        this.Bar = bar
    }
}

Bar being a third class in my application.

My question:

In the KeepReference method, how can I know if the Action passed in parameter refers to an anonymous method (this.Bar = b;) or a concrete method (this.Whatever)?

I checked the properties of action. I couldn't find any property on action (like IsAbstract) of a IsAnonymous kind. The underlying type is MethodInfo which makes sense because after compiling I can see in ildasm the anonymous method "became" a normal method on Foobar. In ildasm I can also see that the anonymous method is not a full pink square but a white square surrounded by pink and in its definition there's a call to some CompilerServices classes but I don't know how to take advantage of this back in C#. I'm sure it's possible to get to know about the real nature of action. What am I missing?

Landed answered 6/10, 2012 at 8:21 Comment(4)
Possible duplicate of How to identify anonymous methods in System.ReflectionMatrona
@MichaelKjörling It looks like the question I was looking for. I'll give it a try. Even if Pavel's answer is disappointing...Landed
Pavel does have a point, though. Still, I think that personally, if I needed something like this (I don't quite see why it'd matter in practice whether a method is anonymous or not), I'd rather rely on a CompilerGeneratedAttribute on the method, than the intricacies of the compiler's anonymous method naming scheme.Matrona
just as a usecase : I'm using some debug helpers that prints the name of the method executed, if applicable.Uppercase
L
4

For the sake of having an "accepted" answer on this question, I went as per the link given by Michael Kjörling in his first comment on my question.

if (action.Target.GetType().GetMethods().Where(method => method.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()).Contains(action.Method))
{
    // ...
}
Landed answered 15/10, 2012 at 17:41 Comment(1)
if (action.Method.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) {...} looks shorter and should perform a little bit fasterAnkara
V
2

Compiler generated methods will always have their names with angled brackets like below

Void <Main>b__0()

so why not just fetch the name and see if it has angled brackets in it.

Action someaction = () => Console.Write("test");
string methodName= RuntimeReflectionExtensions.GetMethodInfo(someaction).ToString();

if(methodName.Contains("<"))
  Console.write("anonymous");

or you can use much better pattern matching with regex

Vernice answered 6/10, 2012 at 9:9 Comment(5)
Is this guaranteed by the C# spec ("will always"), or simply an implementation detail of the compiler? If it is guaranteed by the C# spec, a reference would be nice.Matrona
I thought about this solution but I was hoping the API was providing something "out of the box".Landed
I know that angle brackets are disallowed in identifier names by C# (which does not have any bearing on whether or not they are allowed in identifier names in the CLR), but it's important to keep in mind that different compilers (or even different versions of the same compiler) can generate names in different ways. Unless you can definitely state that a certain behavior is guaranteed by the C# or CLR specs, you shouldn't rely on it because it may change at any time. What if in the next version they decide to use $ (like in Java inner class full names) rather than <>?Matrona
i certainly dont have anything to back up ,but checking for the presence of the compilergeneratedattribute is a much better option.Vernice
There will always be some 'illegal characters' in the anonymous method name, so if you check for a valid function name, and it fails, it will be a generated function.Woolridge

© 2022 - 2024 — McMap. All rights reserved.