Используя 'это' в базовом конструкторе? - PullRequest
16 голосов
/ 26 февраля 2012

Я работаю над проектом, который включает в себя много интерфейсов и наследования, которые начинают становиться немного сложнее, и теперь я столкнулся с проблемой.

У меня есть абстрактный класс State, который принимает объект Game в качестве аргумента конструктора. В конструкторе моего класса Game он принимает состояние State. Идея состоит в том, что при наследовании от абстрактного базового класса Game, при вызове конструктора базового класса, вы даете ему начальный объект State. Однако этот объект состояния принимает ту же игру, в которой вы его создаете. Код выглядит так:

public class PushGame : ManiaGame
{
     public PushGame() :
          base(GamePlatform.Windows, new PlayState(this), 60)
     {
     }
}

Однако это не работает. Я могу только предположить, потому что ключевое слово 'this' не может быть использовано до тех пор, пока конструктор не начнет выполняться. Попытка использовать его в конструкторе вашего базового класса, по-видимому, не работает. Итак, что будет моим лучшим обходным путем для этого? Мой план B состоит в том, чтобы просто удалить аргумент State из конструктора класса Game и потом просто установить состояние внутри кода конструктора.

Есть ли более простой и менее навязчивый способ сделать это?

Ответы [ 4 ]

8 голосов
/ 26 февраля 2012

Очевидно, что класс ManiaGame всегда использует объекты типа PlayState, поэтому вы можете перемещать создание на уровне ManiaGame:

public class PushGame : ManiaGame
{
     public PushGame() : base()
     {
     }

}

public class ManiaGame
{
    PlayState ps;   
    public ManiaGame() {
        ps = new PlayState(this);
    }
}

Если вам нужны более конкретные классы PlayState ..

public class PushGame : ManiaGame
{
     public PushGame() : base()
     {
     }
     protected override PlayState CreatePlayState()
     {
        return new PushGamePlayState(this);
     }
}

public class ManiaGame
{
    PlayState ps;   
    public ManiaGame() {
          ps = CreatePlayState();
    }
    protected virtual PlayState CreatePlayState()
    {
        return new PlayState(this);
    }
}

public class PlayState
{
   public PlayState(ManiaGame mg) {}
}


public class PushGamePlayState : PlayState
{
   public PushGamePlayState(ManiaGame mg) : base(mg){}
}
1 голос
/ 26 февраля 2012

Если используемая реализация State зависит от конкретного класса Game, то я бы создал новый экземпляр State внутри конструктора дочернего класса Game (PushGame) и получил бы доступ к State в базовом классе через свойство abstract.

public class PushGame : ManiaGame
{
    private readonly PlayState gamePlayState;

    public PushGame() : base()
    {
        gamePlayState = new PlayState(this);
    }

    protected override State GamePlayState
    {
        get { return gamePlayState; }
    }
}

public abstract class ManiaGame
{
    protected abstract State GamePlayState { get; }
}

public class State
{
    public State(ManiaGame mg) { }
}


public class PlayState : State
{
    public PlayState(ManiaGame mg) : base(mg) { }
}
1 голос
/ 26 февраля 2012

Из спецификации языка C #

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

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

т.е. this может использоваться только как ключевое слово области видимости до выполнения конструктора:

    : this("ParameterForAnotherConstructor")

Но он не доступен как ссылка на экземпляр класса, так как он не завершил построение

    : base(this) // Keyword this is not available in this context

И, очевидно, мы также не можем вызывать методы экземпляра из инициализатора конструктора

    : base(GetThis()) // Object reference is required

Чтобы решить проблему OP, изменения в базовом классе Mania кажутся неизбежными, учитывая двустороннюю связь между PlayState и ManiaGame (или подклассами ManiaGame, например PushGame). Существует много доступных шаблонов для разделения жестких зависимостей, таких как Принцип инверсии зависимостей (т. Е. Абстракция связи между классами) или шаблон Observer - (один из двух классов вызывает события или разрешает обратные вызовы). быть подключенным (например, делегаты или Action s), позволяя второму классу «наблюдать» изменения состояния без жесткой связи между ними.

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

0 голосов
/ 26 февраля 2012

Различается ли в вашем дизайне игра (скажем, нарды) и игра в процессе (игра в нарды)? Если вы пытаетесь смешать эти два понятия, то я бы предложил смоделировать их по отдельности. Например, Backgammon и BackgammonContest.

...