代码之家  ›  专栏  ›  技术社区  ›  amelvin

C set/get中的堆栈溢出错误

  •  5
  • amelvin  · 技术社区  · 15 年前

    上周五,我在处理一个应用程序的公共评论部分时,遇到了一个堆栈溢出错误,这让我很困惑,所以我想请求帮助。使用“堆栈溢出”表达式搜索Web有点自我挫败!

    在发送要添加到数据库的类的实例之前,我想对类中字段的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的情况下,它能工作吗?这可以用较短的语法实现吗?如果可以,如何实现?

    这是我第一个问题,所以-请尽量温柔!

    8 回复  |  直到 9 年前
        1
  •  21
  •   Andrew Hare    15 年前

    第一个示例的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块才能正确语法。您的第二个示例没有任何问题——是的,它有点冗长,但是它清晰、简洁,并且完成了任务。

        2
  •  8
  •   Aaronaught    15 年前

    getter引用自己(如安德鲁指出的),但setter也是错误的。

    此代码:

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

    …实际上没有设置任何内容。这个 HtmlEncode 方法 返回编码值 ,实际上没有改变 value .

    另一件事你应该记住的是如果你是 HTMLNEN码 -在进去的路上,你需要 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 .把你班的合同弄清楚,它要么履行 二者都 编码 解码或者两者都不做。

        3
  •  4
  •   mattcole    15 年前

    第一个代码中出现堆栈溢出,因为属性getter返回属性本身。

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

        4
  •  2
  •   Scott Dorman    15 年前

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

        5
  •  2
  •   Noldorin    15 年前

    听起来你想用 auto-implemented properties 在C 3.0中引入的特性,但使语法有点混乱。

    返回 FeedbackComment 在的get访问器中 反馈意见 属性正在创建一个自引用循环,它不断地“获取”属性,所以这里的堆栈溢出并不奇怪!

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

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

    在您的例子中,由于您希望在“set”访问器上进行处理,所以使用支持字段的标准方法就是您想要的。

        6
  •  2
  •   shf301    15 年前

    记住,属性实际上是方法-它们的编译器会将它们转换为 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);
        }
    }
    

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

        7
  •  1
  •   Oliver Hanappi    15 年前

    我认为你缺乏对属性的基本理解。属性不能包含任何数据,它只是一对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;
        }
    }
    

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

        8
  •  1
  •   RockWorld    15 年前

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

    问题不在于属性,而在于从以下任何方法中获取值:

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

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