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
shareSnakeGame
对象的子类,获得了对其属性的访问。溢出的起源已在其他答案中确定,其他答案指出了程序草图中的架构设计问题
我想我应该简单地说一下如何自己调试这些问题
第一:当面临堆栈溢出时,几乎总是存在无界递归。当在一个显然没有堆栈溢出的成员中报告溢出时,发生了什么?这种情况发生了:
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来代替。