处置对象时发生 StackOverflowException

StackOverflowException when disposing an object

我创建了一个 class 来将搜索值存储到会话中。这样当我从另一个页面返回搜索页面时,我可以获取存储在会话中的值。当搜索页面加载时,我从会话中获取值,填写文本字段,然后处理对象。但是当我尝试处理该对象时,我得到了一个 WhosebugException 。为什么会发生这种情况,我该如何解决?

    public class SearchValue : IDisposable{
          public int ID {get; set;}
          public string Name {get; set;}

          public void Dispose()
          {
              this.Dispose();
          }
     }

            public void SaveSearchValueToSession()
            {
                SearchValue searchValue = new SearchValue();
                searchValue.ID = Convert.ToInt32(txtID.Text);
                searchValue.Name = txtName.Text;
                Session["SEARCH_VALUE"] = searchValue;
            }

protected void Page_Load(object sender, EventArgs e){
      SearchValue searchValue = (SearchValue)Session["SEARCH_VALUE"];
      txtID.Text = searchValue.ID.ToString();
      txtName.Text = searchValue.Name;

      //Right here I would like to dispose the object
      searchValue.Dispose()
}

您正在从 Dispose 方法内部调用相同的方法。这确实会引起WhosebugException。我想知道你是否真的需要实施 IDisposable 以及你是否理解它的目的...

如果你真的需要:

  1. 赠送一个meaningful implementation of Dispose,免费非托管资源等;
  2. 在变量周围使用using
  3. 处理后不要保存在Session里面。

另请阅读 Fundamentals of Garbage Collection

从评论中我注意到您担心创建太多对象。别担心,这里没问题。尤其如此,因为您的对象(单个)现在没有被垃圾收集:您将它保存在会话中,直到会话被销毁。您一遍又一遍地重复使用同一个对象。

您的问题源于实施 IDisposable 模式的不当方式。这是一个简短但完整的实施示例:

public class ThisIsDisposable : IDisposable
{
    public ThisIsDisposable() { }

    private bool _isDisposed = false;
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (!_isDisposed)
            {
                //Do your unmanaged disposing here
                _isDisposed = true;
            }
        }
    }
}

如果 class 是 sealed,那么第二个 Dispose(bool) 方法应该标记为 private 而不是虚拟的。 IDisposable 的主要用途是处理非托管资源。如果你没有非托管资源,你仍然可以实现它,以便对象可以在 using 语句中使用,但通常 Dispose(bool) 重载在这种情况下不会做任何事情。

参见 CA1063 (https://msdn.microsoft.com/en-us/library/ms244737.aspx) and IDisposable Interface (https://msdn.microsoft.com/library/system.idisposable.aspx)

正如您的评论所说,使用 IDisposable 不会自动释放内存。当不再有对该对象的引用时,您的对象将被垃圾回收。存储对您的值的引用并且从不删除这些引用将导致内存泄漏,因此如果您想确保不泄漏内存,那么您需要确保没有更多对您创建的对象的活动引用(清除 list/session 对象等)。

在您的情况下,完全没有必要实施此模式,因为您的对象未在 using 语句中使用,并且它们不消耗托管资源。

使用会话是一场艰苦的战斗。就个人而言,我只将它们用于帐户登录状态。查询字符串可以帮助您解决问题。请参阅下面的示例。

前页

Response.Redirect("http://yoursite.com/search?id=12");

然后在搜索页面上(假设)(页面加载)

// usual if(!Page.PostBack)

int searchId = -1;
if(Request.QueryString.Count > 0)
{
   String searchIdStr = Request.QueryString["id"]
   if(searchIdStr != String.Empty)
   {
       try{ 
          searchId = Convert.ToInt32(searchIdStr); 
          SearchValue searchValue = searchValueRepo.GetSearchValues()
            .Find(o => o.ID == searchId);
          if(searchValue == null){
             //Failed to find your search values
          }
       } catch (Exception ex) { //failed to convert to integer };

   }
}

然后,如果您觉得用户对查询字符串有太多控制权,您可以设置 id=239847hcjdhnsjk 或加盐哈希,这样就没有人可以尝试更改该值来改变网站的行为.