为什么不能将成员方法传递给基类构造函数?

时间:2011-09-16 16:04:14

标签: c#

class Flarg
{
    private readonly Action speak;

    public Action Speak
    {
        get
        {
            return speak;
        }
    }

    public Flarg(Action speak)
    {
        this.speak = speak;
    }
}

class MuteFlarg : Flarg
{
    public MuteFlarg() : base(GiveDumbLook)
    {
    }

    private void GiveDumbLook()
    {
    }
}

编译器给出错误“非静态字段,方法或属性需要一个对象'Project.Namespace.Class.GiveDumbLook'。

这似乎与将动作作为参数传递给任何其他方法没什么不同。为什么这个无效?

修改 很棒的答案。谢谢大家。我想这只会让我感到困惑,因为它似乎是来自this question的硬币的另一面;最高投票答案明确指出

  

在第一个构造函数运行之前,C#对象已完全构造并初始化为零。

通过该声明,似乎上述代码应该有效。显然存在微妙的差异。

7 个答案:

答案 0 :(得分:23)

像这样改写:

public MuteFlarg() : base(this.GiveDumbLook) { }

现在很清楚为什么你不能。在基类构造函数调用中引用this是不合法的。这不合法,因为它很容易导致错误。派生类的构造函数尚未运行,因此字段未设置为其初始状态(初始状态由构造函数完成运行时的对象状态定义)。

这在说明书的§10.11.1中明确说明:

  

实例构造函数初始值设定项无法访问正在创建的实例。因此,在构造函数初始值设定项的参数表达式中引用this是编译时错误,因为参数表达式通过 simple-name <引用任何实例成员的编译时错误/ em>的

最后一条语句明确禁止通过简单名称this.GiveDumbLook引用GiveDumbLook

答案 1 :(得分:21)

我只是想确保这里有一个明确的要点。

正如Jason正确指出的那样,在构造函数 body 运行之前,在C#中访问“this”或“this”的任何成员(无论是隐式还是显式)都是不合法的。所有字段初始值设定项(甚至是基类上的字段初始值设定项)都在任何构造函数体之前运行,因此在任何字段初始值设定项中使用“this”是不合法的。类似地,“base()”或“this()”构造函数初始化子句在构造函数体之前运行,因此在构造函数初始值设定项的参数中访问“this”或“this”的成员是不合法的。 / p>

我们有这种行为的原因是因为在正文运行之前访问“this”是一个糟糕的编程习惯。很可能导致字段初始化中存在排序依赖性。很可能导致意外地观察到“只读”字段处于未初始化状态。很可能导致虚拟方法调用更多派生方法,这些方法依赖于尚未初始化的状态才能正确运行。所有这些都会产生错误,C#应该是一种可以防止设计错误的语言。基本上,我们不希望你触摸“this”,直到你进入一个方法体,并且可以执行更高级的逻辑,而不仅仅是分配到字段。

在构造函数或字段初始值设定项的任何部分开始运行之前,保证对象完全创建并初始化为其默认状态。 “this”对象肯定存在于字段初始化程序正在运行的位置 - 它必须存在,因为字段初始化程序正在修改它!

简而言之:我们不会让你触及“这个”,因为这样做容易出错,不是因为这样做是不可能的。

在这种特定情况下,您将一个委托的委托传递给基础构造函数,该构造函数可以调用该委托,从而调用可能依赖于尚未创建的状态的方法,因为派生的构造函数体尚未运行。

答案 2 :(得分:10)

只能将静态字段传递给基础构造函数。该对象尚未初始化,因此您无法使用实例成员。如果你使GiveDumbLook静态它将起作用。

答案 3 :(得分:5)

请记住,委托由两部分组成:

  • 调用方法
  • 目标实例

调用基础构造函数时,该方法已知道,但目标实例尚未构建。

因此,您可以省略目标实例,创建一个开放的委托。最简单的方法是使用lambda:

class Flarg
{
    private readonly Action speak;

    public Action Speak
    {
        get
        {
            return this.speak;
        }
    }

    public Flarg(Action<Flarg> speak)
    {
        this.speak = () => speak(this);
    }
}

class MuteFlarg : Flarg
{
    public MuteFlarg() : base(x => ((MuteFlarg)x).GiveDumbLook())
    {
    }

    private void GiveDumbLook()
    {
    }
}

答案 4 :(得分:1)

因为在基类(Flarg)构造的阶段,您没有MuteFlarg类的实例,因此无法访问非静态方法或字段。

答案 5 :(得分:1)

如果尚未构造对象,如何传递非静态成员?

答案 6 :(得分:1)

在创建基础对象时,该对象尚不存在。那么它应该如何制作方法的“指针”呢?