C#中的堆栈溢出错误-如何修复?

9

我遇到了一个非常有趣的运行时错误,它会产生一个堆栈溢出。

我定义了一个结构如下:

public enum EnumDataType { Raspberry, Orange, Pear, Apple };

public class DataRequest
{
    public long DataSize 
    { 
        get { return 0; } 
        set { DataSize = value; } 
    }

    public EnumDataType DataType  
    { 
        get { return EnumDataType.Apple; } 
        set { DataType = value; } 
    }
}

以下代码行完美运行:
```html

以下代码行完美运行:

```
DataRequest request = new DataRequest();
request.DataSize = 60;

然而,当我跨越以下代码行时,它会生成堆栈溢出:
request.DataType = EnumDataType.Raspberry;

当然,我可以通过删除默认值或使用自动获取/设置来解决问题,但我需要它既可读又可写,并返回一个默认值 - 有什么想法吗?

2
我希望调用这两个setter中的任何一个都会导致堆栈溢出,无论您提供了什么值。也许您可以提供一个简短但完整的程序来演示第一个"[work]ing perfectly"? - AakashM
我想每个人在某个时候都做过这个 :) - Callum Rogers
1
我一直觉得当Stackoverflow错误出现时,这些错误非常有趣。因为在我看到它们的几次经历中,它们通常涉及两个或更多方法,这些方法都被美妙地命名为彼此的反函数。它们并不包含任何功能代码,而是相互引用,如果能够正常工作,那将是如此美妙,但事实并非如此。未来还没有到来。 - WonderWorker
5个回答

20
public long DataSize { get { return 0; } set { DataSize = value; } } 

您一直在设置DataSize的值。 您需要创建一个本地变量并使用该变量。例如:

private long dataSize;

public long DataSize
{
    get { return this.dataSize; }
    set { this.dataSize = value; }
}

编辑 我写了DataSize,但同样适用于DataType


+8 但没有解释为什么会发生堆栈溢出? - John Nicholas
2
-1:这仍然会始终返回0,而不是将0作为默认值。诚然,问题并不清楚,但我认为这不是意思。 - Jon Skeet
1
@John Nicholas:这是因为程序试图使用DataSize来设置DataSize,然后又想用DataSize来设置它自己的DataSize,这将再次用DataSize来设置下一个DataSize。接着,程序会用DataSize来设置它自己的DataSize,然后再用DataSize来设置DataSize……哇,令人头痛;我甚至还没到一半! - Spoike
我知道为什么 ;) 原问题提问者却不知道 ;) - John Nicholas
谢谢 - 很棒的答案。我现在完全明白发生了什么。我们不是在处理属性,而是在处理方法,因为它是一个对象。 - Contango
你正在处理属性...但是你的属性是自引用的,所以它们一遍又一遍地调用自己(因此导致堆栈溢出)...属性在某种程度上就像方法(我想在MSIL中它们是相同的),不同之处在于,因为你可以对它们进行赋值,所以在表达式中使用它们更容易。它们还带有不昂贵的暗示/期望,不像方法可能会使用各种资源并花费任意数量的时间来完成。 - John Nicholas

14

正如其他人所说,堆栈溢出是因为您的属性设置器只是在调用自身。如果您将其视为一个方法,可能更容易理解:

// This obviously recurses until it blows up
public void SetDataType(long value)
{
    SetDataType(value);
}

我理解您想创建具有默认值的普通属性,是这样吗?

在这种情况下,您需要使用由setter设置的备份变量 - getter也应返回这些变量。是这些变量应该获得默认值:

private long dataSize = 0;
public long DataSize {
  get { return dataSize; }
  set { dataSize = value; }
}

private EnumDataType dataType = EnumDataType.Apple;
public EnumDataType DataType { 
  get { return dataType; }
  set { dataType = value; }
}

或者,使用自动属性但在构造函数中设置默认值:

public long DataSize { get; set; }
public EnumDataType DataType { get; set; }

public DataRequest()
{
    DataSize = 0; // Not really required; default anyway
    DataType = EnumDataType.Apple;
}

谢谢 - 很棒的回答。我现在完全明白发生了什么。我们不涉及属性,而是方法,因为它是一个对象。我最初提问的原因是如何设置返回的默认值,而你的答案涵盖了这一点。附言:我很喜欢今年年初您在伦敦的演讲 - 您的演示非常有趣,不仅仅是因为袜子木偶 :) - Contango

4

堆栈溢出发生的原因是在setter中你正在将属性设置为一个值(即你正在尝试让某些东西设置自己为某些东西...这会导致无限循环),这意味着它试图将自身设置为一个值,这意味着它一直尝试将自己设置为一个值直到boom。

你的属性永远不会得到你设置的值,因为它们总是返回相同的值(而不是存储的值)。

public enum EnumDataType { Raspberry, Orange, Pear, Apple }; 

public class DataRequest 
{ 
private long _dataSize = 0;
private EnumDataType _dataType = EnumDataType.Apple;

public long DataSize { get { return _dataSize ; } set { _dataSixe= value; } } 
public EnumDataType DataType  { get { return _dataType; } set { _dataType= value; } } 
} 

这是你真正想要的


3

你需要使用后备存储来实现它:

private EnumDataType dataType;
public EnumDataType DataType  { get { return EnumDataType.Apple; } set { dataType = value; } }

每次你在 getter 和 setter 中执行某些操作时都应该这样做。顺便问一下,为什么你可以设置变量?你不能读取它们,你总是得到 EnumDataType.Apple。如果你想要一个起始值,可以这样做:

private EnumDataType dataType = EnumDataType.Apple;
public EnumDataType
{
   get
   {
       return dataType;
   }
   set
   {
       dataType = value;
   }
 }

2
那无法编译 - EnumDataType 是一个非可空值类型。 - Jon Skeet

2

我不理解第一行代码的含义: request.DataSize = 60;

这样做不会导致堆栈溢出 - 我的建议是使用支持属性:

public class DataRequest
{
    protected int dataSize = 0;
    protected EnumDataType enumDataType;
    public long DataSize { get { return 0; } set { dataSize = value; } }
    public EnumDataType DataType  { get { return EnumDataType.Apple; } set { enumDataType = value;} 
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接