【问题标题】:Why does Property Set throw StackOverflow exception?为什么 Property Set 会抛出 StackOverflow 异常?
【发布时间】:2008-12-15 00:09:57
【问题描述】:

我知道 java 并且通常会使用 getter/setter 方法。我有兴趣使用以下代码在 C# 中执行此操作,但它会引发 StackOverflow 异常。我做错了什么?

调用代码

c.firstName = "a";

物业代码

public String firstName;
{
    get
    {
        return firstName;
    }
    set
    {
        firstName = value;
    }
}

【问题讨论】:

    标签: c#


    【解决方案1】:

    这是因为您在递归调用该属性 - 在 set 中您再次设置该属性,这将继续无限直到您破坏堆栈。

    您需要一个私有的支持字段来保存该值,例如

    private string firstName;
    
    public string FirstName
    {
        get
        {
            return this.firstName;
        }
        set
        {
            this.firstName = value;
        }
    }
    

    或者,如果您使用 C# 3.0,您可以使用自动属性,它会为您创建一个隐藏的支持字段,例如

    public string FirstName { get; set; }
    

    【讨论】:

    • 不知道怎么打电话给hidden backing field。感谢您提供自动属性。
    • 我不敢相信这是一个解决方案......我对 C# 的信心现在几乎消失了。
    • 哎呀,如果你认为这很糟糕,我不想看到你写 Java。
    • 能否请您解释一下,它是如何炸毁堆栈的?是不是同一个值在 setter 中徘徊?
    • @ArliChokoev 不,注意大小写:属性称为FirstName,成员变量称为firstName。为了减少这种混淆,许多开发人员更喜欢在成员前面加上_m_,这样会更明显。
    【解决方案2】:

    您在属性中设置属性名称,而不是字段名称。这样会更好:

    private string m_firstName;
    
    public String firstName;
    {
        get
        {
            return m_firstName;
        }
        set
        {
            m_firstName = value;
        }
    }
    

    【讨论】:

      【解决方案3】:

      StackOverflowExeption 在您的 sn-p 中是不可避免的,这就是原因。

      要理解这一点,我们必须知道什么是属性以及我们使用它们的原因。基本上,属性是一组方法,它不是一个字段。通过“属性”,我们表示 set 方法(设置某物的值)和 get 方法(返回某物的值)的集合。

      在借助上述方法为私有字段赋值时,属性为我们提供了一定程度的灵活性。

      您的代码不起作用,因为您通过一次又一次调用 get 方法溢出堆栈,这就像一个没有任何退出条件的递归函数(当我们尝试获取 FirstName 的值时,该属性调用它的 get 方法,它返回itsels,然后再次调用get,再次调用get,再一次,再一次,再一次,你的堆栈被这些东西填满了)。

      如果你的类中有一个私有字段,你可以使用该字段周围的属性来公开它,我们可以为 get 和 set 方法添加额外的处理,这里是一个例子:假设我们想要获取 ascii 代码一个数字(0 到 9),我们可以为此使用一个属性(这不是最好的例子,只是为了说明一些属性的使用):

      // this is the private field that we do not want to expose (aka to make
      // it accessible for everyone
      private int digit;
      
      // But we want somehow to allow users of our class to interact 
      // with the digit field, so we create a property.
      // Note: fields' name start with lowercase, properties' name with Uppercase
      public int Digit
      {
         get 
         {
             // this is the method that gets calls whenever the user calls Digit,
             // Example: Console.WriteLine(object.Digit);
      
             // let's add here the logic of getting digit's ascii code
             return Char.Parse(digit.ToString());
         }
      
         set
         {
             // this method gets called when someone assigns a value for Digit
             // Example: Digit = 3;
           
             // here we can add the validation logic ( 0 <= value <=9)
             if(value < 0 || value > 9)
                throw new Exception("Please provide a number between 0 and 9");
          
             digit = value;
          }
      }  
      

      当有人访问数字时,我们使用 Digit 属性将数字转换为 ASCII,并验证尝试分配给数字字段的值。

      在您的情况下,您可以像这样编写 FirstName 属性:

      public string FirstName{  get; set; }
      

      在后台,这条线将被视为:

      private string firstName;
      public string FirstName
      {
         get { return firstName; }
         set { firstName= value }
      }
      

      更详细地说明堆栈溢出的原因。

      有许多语言没有像 C# 这样的属性,所以人们只需添加单独的 get 和 set 方法。有了这个模型,就更容易发现溢出发生的位置。假设 C# 中没有属性,因此我们创建了一个私有字段和两个方法 - 一个用于设置,另一个用于获取私有字段的值。

      private string firstName;
      
      public string GetFirstName()
      {
          return firstName;
      }
      
      public void SetFirstName(string value)
      {
         firstName = value;
      }
      

      现在他有一个 setter 和 getter,它们没有包含在属性中。要查看问题示例中发生溢出的位置,让我们将其调整到我们没有属性的新环境;

      public string GetFirstName()
      {
         return GetFirstName();
      }
      
      public void SetFirstName(string value)
      {
         GetFirstName() = value; 
      }
      

      这实际上就是您的代码所做的。没有私人场地可以玩,物业自己玩^^

      当我们写这个时:

      object.SetFirstName("Rick Astley");
      

      我们进入 SetFirstName 方法,该方法调用 GetFirstName(),voala,死路一条。将“=>”视为“调用”:

      SetFirstName("Rick Astley") => GetFirstName() => GetFirstName() => ...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-28
        • 2013-09-04
        • 1970-01-01
        相关资源
        最近更新 更多