C# Getter C上的StackOverflowException#

C# Getter C上的StackOverflowException#,c#,uwp,C#,Uwp,我在get上得到一个StackOverflowException public abstract class SenseHatSnake { private readonly ManualResetEventSlim _waitEvent = new ManualResetEventSlim(false); protected SenseHatSnake(ISenseHat senseHat) { SenseHa

我在
get上得到一个StackOverflowException

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意味着我在某个地方有一个无限循环或无限递归,但我真的不知道在哪里,也不知道如何解决它。

SnakeGame
中的这一行导致了递归

public readonly Movement Movement = new Movement(SenseHat);
由于
移动
是从
蛇类游戏
继承而来的,因此它的构造函数将初始化
蛇类游戏
,再次调用上面的行来初始化它自己的
移动
字段。这会导致递归。

这是一种不好的模式:

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

protected static ISenseHat SenseHat { get; set; }
//        ^^^^^^
您的构造函数正在设置一个在
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
share
SnakeGame
对象的子类,获得了对其属性的访问。

溢出的起源已在其他答案中确定,其他答案指出了程序草图中的架构设计问题

我想我应该简单地说一下如何自己调试这些问题

第一:当面临堆栈溢出时,几乎总是存在无界递归。当在一个显然没有堆栈溢出的成员中报告溢出时,发生了什么?这种情况发生了:

void Bad()
{
    Good();
    Bad();
}
如果调用
Bad
,则它将堆栈溢出,但大多数情况下溢出将在
Good
中报告,因为
Good
可能比任何单个调用
Bad
使用更多堆栈


您需要做的是查看调用堆栈,因为它将是
Good/Bad/Bad/Bad/Bad…
,这告诉您执行无限递归的是
Bad
找到递归源的关键是找到直接或间接调用自身的对象。使用您可以使用的仪器进行此操作;异常包含对调用堆栈的跟踪。

诊断此问题的最佳方法是调试代码并检查调用堆栈。您需要做出调整并缩小问题范围。@SelmanGençI不能。我正在raspberry Pi上部署,这是我得到的唯一信息。@LuukWuijster所以你不能在控制台应用程序中复制此代码并调试它?@Luuk:我不同意,创建MVC绝对是可能的。为了演示的目的,我在上创建了一个完整且可验证的示例。为了更容易识别我是如何使用您发布的代码创建了一个完整的示例,我特意省略了将其最小化。无论如何,我个人建议使用二进制搜索来调查难以简化的问题中的问题。如果你将我的代码和你的代码粘贴到一个diff工具中,你会注意到中间的部分是相同的。我不知道为什么这个答案被否决了,这是正确的,因为这是堆栈溢出的根源。我不太明白为什么这是一个递归,因为
newmovement(SenseHat)
调用
SenseHat
的getter,然后在几次嵌套调用之后,
SenseHatSnake
的构造函数设置
SenseHat
调用setter。这是两种不同的方法。@OlivierJacot Descombes movement的构造函数调用SnakeGame的构造函数,在初始化snake游戏时,代码输入
movement=new movement(SenseHat)再次出现,因为字段也在构造函数中初始化。所以蛇的构造函数调用这一行,这一行调用蛇的构造函数。好的,我明白了。我专注于
SenseHat
属性。这与这项财产无关。
移动
构造函数正在间接调用自身。OP的堆栈溢出意外发生在
SenseHat
属性中,因为堆栈几乎已满。这样做会在
Movement=new Movement(SenseHat)上产生堆栈溢出异常 @ LuukWuijster。看来你需要重新思考继承策略:继承层次结构中的一个类(<代码> SnaKeGAME )不应该构造它的子类(<代码>运动< /代码>)作为其初始化的一部分。如果您是为了共享字段而进行继承,则应该共享继承层次结构之外的对象中的数据,然后共享该对象(即使用包含而不是继承)。我首先做的是从
SenseHatSnake
继承所有类。然而,这也导致了StackOverflow异常,因为我需要其他类,它们需要相同的类等等。比如
class1{class2=new class2()}class2{class1=new class1()}
做这样的事情有什么好方法?@LuukWuijster看一下编辑,这应该能让你了解我想进行的重组。嗯。。。好的,我明白了。但是我想我会尝试使用DI来代替。