StackOverflowException на Getter C # - PullRequest
       1

StackOverflowException на Getter C #

0 голосов
/ 31 августа 2018

Я получаю StackOverflowException для get; свойства в абстрактном классе.

public abstract class SenseHatSnake
    {

        private readonly ManualResetEventSlim _waitEvent = new ManualResetEventSlim(false);

        protected SenseHatSnake(ISenseHat senseHat)
        {
            SenseHat = senseHat;
        }

        protected static ISenseHat SenseHat { get; set; } // This Line

        public virtual void Run()
        {
            throw new NotImplementedException();
        }

        protected void Sleep(TimeSpan duration)
        {
            _waitEvent.Wait(duration);
        }

    }

Я устанавливаю и получаю это здесь:

public class SnakeGame : SenseHatSnake
{
    private readonly int _gameSpeed = 1000;
    private static Timer _updatePositionTimer;
    private bool _gameOver = false;

    public readonly Movement Movement = new Movement(SenseHat);
    public readonly Food Food = new Food(SenseHat);
    public readonly Body Body = new Body(SenseHat);
    public readonly Display Display = new Display(SenseHat);
    public readonly Draw Draw = new Draw(SenseHat);

    public SnakeGame(ISenseHat senseHat)
        : base(senseHat)
    {
    }
    //More code
}

Один из этих классов выглядит так:

public class Movement : SnakeGame
{

    public Movement(ISenseHat senseHat)
        : base(senseHat)
    {
    }
    //More code
}

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

Ответы [ 3 ]

0 голосов
/ 31 августа 2018

Это плохая схема:

protected SenseHatSnake(ISenseHat senseHat)
{
    SenseHat = senseHat;
}

protected static ISenseHat SenseHat { get; set; }
//        ^^^^^^

Ваш конструктор устанавливает поле static , общее для всех подклассов SenseHatSnake, что означает, что последний класс, устанавливающий поле, "выигрывает". Это также означает, что вы никогда не сможете установить это поле, потому что для создания значения, которое нужно присвоить полю, вы должны создать объект, для которого должно быть установлено это поле - змея, преследующая свой собственный хвост. Также вы не можете получить Movement из класса, который создает член типа Movement как часть его инициализации.

Исправление требует серьезной реорганизации ваших классов:

public class SnakeGame {
    private readonly int _gameSpeed = 1000;
    private static Timer _updatePositionTimer;
    private bool _gameOver = false;

    public Movement Movement {get;}
    public Food Food {get;}
    public Body Body {get;}
    public Display Display {get;}
    public Draw Draw {get;}
    public SnakeGame(ISenseHat senseHat)
    {
        Movement = new Movement(this);
        Food = new Food(this);
        Body = new Body(this);
        Display = new Display(this);
        Draw = new Draw(this);
    }
    //More code
}

public abstract class GameObject {
    protected readonly SnakeGame game;
    protected GameObject(SnakeGame game) {
        this.game = game;
    }
}

public class Movement : GameObject
{
    public Movement(SnakeGame game)
    : base(senseHat)
    {
    }
    //More code
}

Теперь подклассы GameObject делятся SnakeGame объектом, получая доступ к его свойствам.

0 голосов
/ 31 августа 2018

Источник переполнения был указан в других ответах, и другие ответы указали на проблемы архитектурного дизайна в эскизе вашей программы.

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

Во-первых: при переполнении стека практически всегда существует неограниченная рекурсия. Что произошло при переполнении в элементе, который явно не имеет переполнения стека? Это случилось:

void Bad()
{
    Good();
    Bad();
}

Если вызывается Bad, то он будет переполняться стеком, но большую часть времени будет сообщаться о переполнении в Good, поскольку Good, вероятно, использует больше стека чем любой отдельный звонок на Bad.

Что вам нужно сделать, это посмотреть на стек вызовов , потому что это будет Good / Bad / Bad / Bad / Bad ..., и это говорит вам, что Bad выполняет неограниченную рекурсию. Ключом к поиску источника рекурсии является нахождение того, что на самом деле вызывает себя, прямо или косвенно . Используйте инструменты, имеющиеся в вашем распоряжении, чтобы сделать это; исключение содержит трассировку стека вызовов.

0 голосов
/ 31 августа 2018

Эта строка в SnakeGame вызывает рекурсию

public readonly Movement Movement = new Movement(SenseHat);

Поскольку Movement наследуется от SnakeGame, его конструктор инициализирует SnakeGame, снова вызывая строку выше, чтобы инициализировать свое собственное поле Movement. Это приводит к рекурсии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...