【问题标题】:Stack overflow error in C# set/get [duplicate]C# set/get 中的堆栈溢出错误 [重复]
【发布时间】:2010-03-07 22:19:29
【问题描述】:

周五,我在处理应用程序的公共 cmets 部分时遇到堆栈溢出错误,这让我很困惑,所以我想我会寻求帮助。而且使用“堆栈溢出”这个表达式搜索网络有点弄巧成拙!

在发送要添加到数据库的类实例之前,我想对类中字段的 set 语句执行 HtmlEncode:

public class Feedback
{

    public Feedback() { }

    public string FeedbackComment
    {
        get { return FeedbackComment; }
        set {System.Web.HttpUtility.HtmlEncode(value); }
    }

    // other fields 

    // methods
}

这导致 StackOverflow 错误,我已通过将代码更改为如下所示来修复错误:

public class Feedback
{

    public Feedback() { }

    private string feedbackComment;

    public string FeedbackComment
    {
        get { return feedbackComment; }
        set { feedbackComment = System.Web.HttpUtility.HtmlEncode(value); }
    }

    // other fields 

    // methods
} 

但我只是想解释一下为什么第一个 get/set 语句如此递归以至于它们导致堆栈溢出,但是当恢复代码看起来更像 c#2.0 工作时?这可以通过更短的语法来实现吗?如果可以,如何实现?

这是我关于 SO 的第一个问题 - 请尽量保持温和!

【问题讨论】:

  • 我不禁认为这个网站是这个问题的完美场所
  • 这么多正确答案 - 感谢所有帮助。

标签: c# asp.net exception


【解决方案1】:

第一个示例的 getter 是返回属性本身,而不是支持字段。

// The property name is "FeedbackComment"
public string FeedbackComment
{
    // And here you are returning "FeedbackComment" which is
    // creating the stack overflow
    get { return FeedbackComment; }
}

不幸的是,没有办法缩短你所拥有的,自动实现的属性(即public String FeedbackComment { get; set; })必须有空的 getter 和 setter 块才能在语法上正确。您的第二个示例没有任何问题 - 是的,它有点冗长,但清晰、简洁,并且可以完成工作。

【讨论】:

  • +1 感谢您的建议,第二个示例是一个合理的选择。
【解决方案2】:

getter 引用自身(正如 Andrew 指出的那样),但 setter 也是错误的。

这段代码:

set { System.Web.HttpUtility.HtmlEncode(value); }

...实际上并没有设置任何东西。 HtmlEncode 方法返回编码值,它实际上并没有改变value

你应该记住的另一件事是,如果你是HtmlEncode-ing,你需要HtmlDecode,否则你可能会得到多个编码(不是幂等的) .如果你试图“自动化”编码过程,那么这个类通常看起来像这样:

public class Foo
{
    private string bar;

    public string Bar
    {
        get { return HttpUtility.HtmlDecode(bar); }
        set { bar = HttpUtility.HtmlEncode(value); }
    }

    public string SafeBar
    {
        get { return bar; }
    }
}

或者你可以反转安全/不安全的逻辑,例如:

public string Bar
{
    get { return bar; }
    set { bar = HttpUtility.HtmlEncode(value); }
}

public string UnsafeBar
{
    get { return HttpUtility.HtmlDecode(value); }
}

无论哪种方式,你的类都应该明确表明它正在执行某种编码,否则如果你编写这样的代码:

Foo foo1 = new Foo();
foo1.Bar = "<test>";
Foo foo2 = new Foo();
foo2.Bar = foo1.Bar;

...然后您将开始在foo2.Bar 的输出中看到一堆难看的转义字符。明确您的班级合同,它应该执行 both 编码 解码,或者两者都不执行。

【讨论】:

  • +1 我最初确实有一个解码器,但为了简化示例,我将其省略了。我同意你所有的cmets。
【解决方案3】:

您在第一个代码中遇到了堆栈溢出,因为您的属性 getter 正在返回属性本身。

这将导致 getter 再次被调用,直到堆栈溢出为止。

【讨论】:

    【解决方案4】:

    导致 StackOverflowException 的代码没有为属性提供支持字段,get 访问器正在返回属性本身,这就是导致堆栈溢出的原因。第二个示例确实提供了一个支持字段并返回其内容。

    【讨论】:

      【解决方案5】:

      听起来您正在尝试使用 C# 3.0 中引入的auto-implemented properties 功能,但语法有点混乱。

      FeedbackComment 属性的 get 访问器中返回 FeedbackComment 会创建一个自引用循环,该循环会不断“获取”该属性,因此那里的堆栈溢出不足为奇!

      自动实现属性的正确语法如下。但是,它不能在 get 或 set 访问器中执行任何处理(实际上是根据定义)。

      public class Feedback
      {
          public Feedback() { }
      
          public string FeedbackComment
          {
              get;
              set;
          }
      
          // other fields 
      
          // methods
      }
      

      在您的情况下,由于您想对“set”访问器进行处理,因此使用支持字段的标准方式就是您想要的。

      【讨论】:

        【解决方案6】:

        请记住,属性是真正的方法 - 它们编译器会将它们转换为 T get_Property()set_Property(T value) 调用 - 没有存储(除非使用自动属性,但是编译器会自动创建一个支持字段。所以呢?您的堆栈溢出示例如下所示:

        public class Feedback
        {
        
            public Feedback() { }
        
            // Getter
            public string get_FeedBackComment() {
                return get_FeedBackComment();
            }
            // Setter
            public void set_FeedBackComment(string value) {
                 System.Web.HttpUtility.HtmlEncode(value);
            }
        }
        

        所以你得到的是一个函数调用,它永远调用它自己,所以堆栈溢出,因为每次调用都是堆栈推送。该设置调用了一个函数,但从未将其值存储在任何地方。

        【讨论】:

        • +1 是的,仅为默认 {get;set;} 创建的支持字段现在已清除。
        【解决方案7】:

        我认为您缺乏对属性的基本理解。属性不能保存任何数据,它只是一对 getter 方法和一个 setter 方法(也有只有 getter 或 setter 的属性)。

        getter 方法基本上是一种不带参数并返回属性类型值的方法,另一方面,setter 是一种不带返回值的方法,并且有一个属性类型的参数称为 value。 C# 隐藏了这两种方法并将它们组合成一个属性,您可以像普通字段一样调用它们。

        你的第一个实现相当于:

        public class Feedback
        {
            public string get_FeedbackComment()
            {
                return get_FeedbackComment();
            }
        
            public void set_FeedbackComment(string value)
            {
                System.Web.HttpUtility.HtmlEncode(value);
            }
        }
        

        您现在可能会看到递归在哪里以及您的错误在哪里。此外,当您查看 setter 时,您会注意到它没有设置任何内容。 HtmlEncode 的返回值不会保存在任何地方。您需要提供一个支持字段(就像您的第二段代码中的那个)。

        但是,在 C# 3.0 及更高版本中,有一些自动实现的属性,您可以通过以下方式声明这些属性。另请注意,C# 编译器将自动创建一个支持字段,因此两种方式基本上是相同的,但您可以通过第一种方式获得更大的灵活性,因为使用自动实现的属性,您无法添加比简单设置和检索值更复杂的行为(至少在您声明它的类中,使其成为虚拟打开了在子类中扩展属性逻辑的​​可能性..)。

        public class Feedback
        {
            public string FeedbackComment
            {
                get;
                set;
            }
        }
        

        最好的问候,
        奥利弗·哈纳皮

        【讨论】:

        • 这个例子被简化了一点,所以如果这造成了其他问题,那也是我的错!我的部分问题是为什么只为默认 {get; set;} 访问器 - 答案似乎是“设计使然”。
        【解决方案8】:

        这是阐述滥用递归编程的一个很好的例子;)

        问题不在于属性,而是通常从任何方法获取值,例如:

        public int GiveMeValue() { return GiveMeValue(); } public void SetValue(int value) { SetValue(value); }

        无论如何,DotNet 属性是特殊类型的方法。不是吗?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-01-17
          • 2012-05-21
          • 1970-01-01
          • 2020-11-14
          • 2016-11-11
          • 2013-12-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多