如何知道一个操作是否是匿名方法?

8

这是 C# 4.0。

我有一个类,它存储了一些 ActionWeakReference,就像这样:

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

这个类还有另一个方法,允许稍后执行传递给它的Action,但在这个问题中这并不重要。

我这样使用这个类:

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 是我的应用程序中的第三类。

我的问题:

KeepReference方法中,我如何知道传入参数的Action是指匿名方法(this.Bar = b;)还是一个具体的方法(this.Whatever)?

我检查了action的属性。我找不到任何关于action的属性(例如IsAbstract)或者IsAnonymous之类的。底层类型是MethodInfo,这很有意义,因为编译后我可以在ildasm中看到匿名方法“变成”了Foobar上的普通方法。在ildasm中,我还可以看到匿名方法不是一个完整的粉色正方形,而是被粉色包围的白色正方形,在其定义中调用了一些CompilerServices类,但我不知道如何在C#中利用它。我相信有可能了解action的真实性质。我错过了什么?


可能是如何在System.Reflection中识别匿名方法的重复问题。 - user
@MichaelKjörling 看起来这就是我在寻找的问题。我会尝试一下。即使 Pavel 的答案让人失望... - Guillaume
然而,Pavel的观点确实有道理。尽管如此,我个人认为,如果我需要类似这样的东西(我不太明白一个方法是否匿名在实践中有什么影响),我宁愿依赖于方法上的“CompilerGeneratedAttribute”,而不是编译器的匿名方法命名方案的复杂性。 - user
举个例子:我正在使用一些调试助手,如果适用的话,会打印出执行的方法名称。 - Pac0
2个回答

4

为了在这个问题上有一个“被接受”的答案,我按照Michael Kjörling在我的问题上的第一条评论中给出的链接进行了操作。

if (action.Target.GetType().GetMethods().Where(method => method.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()).Contains(action.Method))
{
    // ...
}

如果(action.Method.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) {...} 看起来更短,应该执行得更快。 - Evgeny Safonov

2

编译器生成的方法的名称总是带有尖括号,如下所示

Void <Main>b__0()

那为什么不直接获取名称并查看其中是否包含尖括号呢?

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

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

您可以使用更好的正则表达式进行模式匹配。

2
这是否由C#规范保证(“将始终”),还是编译器的实现细节?如果C#规范保证了它,提供一个参考会很好。 - user
我考虑过这个解决方案,但我希望API能够提供一些“开箱即用”的东西。 - Guillaume
1
我知道在C#中,尖括号不能用于标识符名称(这与它们是否允许在CLR中的标识符名称无关),但重要的是要记住不同的编译器(甚至是同一编译器的不同版本)可以以不同的方式生成名称。除非您能确定某种行为由C#或CLR规范保证,否则不应该依赖它,因为它可能随时发生变化。如果在下一个版本中他们决定使用$(例如Java内部类全名)而不是<>会怎么样? - user
我确实没有任何东西可以支持这个说法,但检查编译器生成的属性是否存在是一个更好的选择。 - Prabhu Murthy
匿名方法名称中总会存在一些“非法字符”,因此如果您检查一个有效的函数名称并失败了,那么它就是一个生成的函数。 - Robert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接