0

在尝试从匿名函数转换为方法组时,我偶然发现了一个奇怪的问题。下面的示例完美地说明了这个问题 - 有 2 种方法 - Run() 和 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;
        }
    }
}
4

2 回答 2

1

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

于 2018-01-20T00:05:35.460 回答
1

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

如果你看匿名方法的定义,“它们是没有名字的方法。我们可以写一个没有名字的小方法块,并将它的地址分配给委托对象,然后用这个委托对象来调用它。”

于 2018-01-20T00:12:05.280 回答