【问题标题】:Anonymous function vs Method group - delegate to an instance method cannot have null this匿名函数与方法组 - 委托给实例方法不能有 null this
【发布时间】:2018-03-10 05:51:12
【问题描述】:

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

【问题讨论】:

  • 您实际上是在尝试在Crash 中使用特定的(或缺少的)cat 实例。你怎么不期望那会出错? lambda 之所以有效,是因为 lambda 在被调用之前不是可执行代码。由于您在实际调用 lambda 之前实例化了实例,因此没有错误。如果你交换这些行——初始化和调用——那么你应该看到同样的错误(NRE)。您可能会收到关于潜在 NRE 的 lambda 用法的 IDE 警告。
  • 感谢您的解释,但我仍然感到困惑。在崩溃示例中 - cat.GetName 被分配给一个 func,我假设在调用该 func 之前不会执行它。这不正确吗?我知道在初始化 cat 之前简单地调用 var myVar = cat.GetName() 会导致 NRE。
  • 我想我的问题很简单——以下 2 个语句是否不相同: Func myFunc = () => myObject.DoStuff(); Func myFunc = myObject.DoStuff;
  • 另外,IIRC,lambda 正在围绕cat 创建一个闭包。这允许 lambda 在 cat 实例化后访问更新的引用。
  • @Ross 他们不一样

标签: c# .net anonymous-function method-group


【解决方案1】:

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

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

【讨论】:

  • 一个很好的解释!非常感谢 - 现在更有意义了:)
【解决方案2】:

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

【讨论】:

  • 感谢您的详细解释!
猜你喜欢
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2010-11-10
  • 1970-01-01
  • 2013-08-13
  • 2011-02-08
相关资源
最近更新 更多