Подтвердить что ты не робот

Почему метод-член не может быть передан в конструктор базового класса?

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 '.

Это ничем не отличается от передачи действия в качестве параметра любому другому методу. Почему это недействительно?

Edit Отличные ответы. Спасибо всем. Думаю, это меня просто смущает, потому что кажется, что это противоположная сторона из этого вопроса; где самый высокий проголосовавший ответ четко заявляет

Объект С# полностью сконструирован и инициализирован до нуля до запуска первого конструктора.

В этом утверждении кажется, что приведенный выше код должен работать. По-видимому, есть тонкая разница.

4b9b3361

Ответ 1

Перепишите это следующим образом:

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

и теперь понятно, почему вы не можете. Недействительно ссылаться на this в вызове конструктора базового класса. Это не законно, потому что это может легко привести к ошибкам. Конструктор для производного класса еще не запущен, и поэтому поля не устанавливаются в их исходное состояние (начальное состояние определяется состоянием объекта при выполнении конструктора).

Это явно указано в п. 10.11.1 спецификации:

Инициализатор конструктора экземпляра не может получить доступ к создаваемому экземпляру. Поэтому ошибка времени компиляции ссылается на this в выражении аргумента инициализатора конструктора, так же как и ошибка времени компиляции для выражения аргумента для ссылки на любой экземпляр элемента через простое имя.

Последнее утверждение явно запрещает ссылаться на this.GiveDumbLook на его простое имя GiveDumbLook.

Ответ 2

Я просто хочу убедиться, что здесь тонкая точка понятна.

Как правильно отмечает Джейсон, в С# не разрешается доступ к "this" или любому члену "this", как неявно, так и явно, до того, как тело конструктора будет запущено. Все инициализаторы полей (даже те, что основаны на базовых классах) выполняются перед любым конструктором, поэтому недействительно использовать "this" в любом инициализаторе поля. Точно так же предложение инициализатора конструктора "base()" или "this()" выполняется перед телом конструктора, и поэтому нет права доступа к "this" или члену "this" в аргументе инициализатора конструктора.

Причина, по которой мы имеем такое поведение, заключается в том, что доступ к "this" до запуска тела - это плохая практика программирования. Это, скорее всего, приведет к тому, что в инициализации поля будут упорядочивающие зависимости. Скорее всего, это приведет к тому, что поля "readonly" будут случайно обнаружены в их неинициализированном состоянии. Это, скорее всего, приведет к вызову виртуальных методов для более производных методов, которые будут зависеть от не-инициализируемого состояния для правильной работы. Все это делает ошибки, а С# - языком, который предотвращает ошибки по дизайну. В принципе, мы не хотим, чтобы вы касались "this", пока не находитесь в теле метода, и можете выполнять более сложную логику, чем просто назначение поля.

Нельзя получить доступ к "this", потому что "базовый объект еще не создан" или что-то подобное. Перед тем, как начнется выполнение любой части конструктора или инициализатора поля, объект будет полностью создан и будет инициализирован до состояния по умолчанию. Объект "this", конечно, существует в той точке, где инициализаторы поля запущены - он должен существовать, потому что инициализаторы поля изменяют его!

Вкратце: мы не позволяем вам касаться "this", потому что делать это подвержено ошибкам, а не потому, что делать это невозможно.

В этом конкретном случае вы передаете делегат методу базовому конструктору, который мог бы вызвать этого делегата, тем самым вызывая метод, который может зависеть от состояния, которое еще не было создано, потому что более производное тело конструктора имеет еще не запущен.

Ответ 3

В базовый конструктор можно передать только статические поля. Объект еще не инициализирован, поэтому вы не можете использовать экземпляры экземпляра. Если вы сделаете статический GiveDumbLook, он будет работать.

Ответ 4

Помните, что делегат состоит из двух частей:

  • метод вызова
  • целевой экземпляр

При вызове базового конструктора метод известен, но целевой экземпляр еще не сконструирован.

Итак, вы можете опустить целевой экземпляр, создавая открытый делегат. Самый простой способ сделать это - использовать лямбда:

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()
    {
    }
}

Ответ 5

Поскольку на этапе построения базового класса (Flarg) у вас нет экземпляра класса MuteFlarg, поэтому он не может обращаться к не статическим методам или полям.

Ответ 6

Как вы можете передать нестационарный член, когда объект еще не сконструирован?

Ответ 7

объект еще не существует в момент создания базового объекта. так как он должен сделать "указатель" на метод?