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

时间:2010-03-07 22:19:30

标签: c# asp.net exception

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

我想在发送要添加到数据库的类的实例之前,在类的字段的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 个答案:

答案 0 :(得分:21)

第一个示例的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 :(得分:8)

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

此代码:

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

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

你应该记住的另一件事是,如果你正在进行HtmlEncode,你需要在出路时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的输出中看到一堆丑陋的转义字符。明确你的课程合同,它应该执行 编码解码,或者不执行任何操作。

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:2)

听起来您正在尝试使用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'访问器进行处理,那么使用支持字段的标准方法就是您想要的。

答案 5 :(得分:2)

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

所以你的获取是一个永远称为自我的函数调用,因此堆栈溢出,因为每次调用都是一个堆栈推送。并且该设置称为函数,但从未将其值存储在任何位置。

答案 6 :(得分:1)

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

最诚挚的问候,
Oliver Hanappi

答案 7 :(得分:1)

这是详细说明误用递归编程的好例子;)

问题不在于属性,而在于通常使用以下任何方法获得价值:


public int GiveMeValue()
{
  return GiveMeValue();
}

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

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