匿名函数与方法组 - 委托给实例方法不能有 null this

Anonymous function vs Method group - delegate to an instance method cannot have null this

我刚刚在尝试从匿名函数转换为方法组时偶然发现了一个奇怪的问题。下面的示例完美地说明了这个问题 - 有 2 种方法 - 运行() 和 Crash() 它们做的一切都是一样的,除了使用匿名函数与方法组。

基本上,问题是我想创建一个 Func 来调用一个对象的方法,该对象在应用程序启动时未初始化,而是稍后出现,在此 func 之前 运行。使用匿名函数效果很好,但转换为方法组会导致异常。

坚持使用匿名函数而不是方法组并不是什么大问题,但是出现了 roslynator 警告 RCS1207,我想先了解这个问题的原因,然后再简单地通过内联注释禁用它.

namespace AnonymousFunctionVsmethodGroup
{
    class Program
    {
        static void Main(string[] args)
        {
            var app = new App();
            app.Run();
            app.Crash();
        }
    }

    public class App
    {
        private Func<string> m_Func;

        public void Run()
        {
            Entity cat = null;

            // Anonymous function. At this point cat is null
            m_Func = () => cat.GetName();

            // Initializing new cat
            cat = new Entity("Cat");

            // Func is executed on a valid cat
            Console.WriteLine(m_Func());
            Console.Read();
        }

        public void Crash()
        {
            Entity cat = null;

            // Method group. At this point cat is null. Code never gets through here and an exception is thrown instead.
            // "Delegate to an instance method cannot have null this"
            m_Func = cat.GetName;

            // Initializing new cat
            cat = new Entity("Cat");

            // Func is executed on a valid cat?
            Console.WriteLine(m_Func());
            Console.Read();
        }
    }

    // Sample entity
    public class Entity
    {
        private string name;

        public Entity(string name)
        {
            this.name = name;
        }

        public string GetName()
        {
            return name;
        }
    }
}

() => GetName() 是一个 lambda 或讨厌的函数。编译器用这种语法做了一些聪明的事情,实际上在你写的 class 上创建了另一个方法(这是一个巨大的过度简化,但它现在会做,查看闭包以获取更多信息)。 lambda 中的代码被移动到这个新方法中,并且仅在您评估 lambda 时调用,即当您调用您的 func 时。而使用 cat.GetName 传递的方法属于 cat 的一个实例,该实例为空,因此此时您无法访问它

当您分配一个 "Method Group" 时,将立即调用与该对象关联的委托。但是,lambda 创建了一个额外的重定向:该代码将首先调用存储对可能不存在的对象上的方法的匿名引用,并采用零个、一个或多个参数,然后使用该参数调用 Facade.Entity.GetName。在您的情况下,它不会将任何参数传递给方法委托,但只要您不执行匿名方法,它就只代表 class.[=10= 上的方法调用的签名]

如果您查看匿名方法的定义,"they are methods without a name. We can write small method block without any name and assign its address to the delegate object, then this delegate object is used to call it."