处置对象时发生 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
以及你是否理解它的目的...
如果你真的需要:
- 赠送一个meaningful implementation of
Dispose
,免费非托管资源等;
- 在变量周围使用
using
;
- 处理后不要保存在
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
或加盐哈希,这样就没有人可以尝试更改该值来改变网站的行为.
我创建了一个 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
以及你是否理解它的目的...
如果你真的需要:
- 赠送一个meaningful implementation of
Dispose
,免费非托管资源等; - 在变量周围使用
using
; - 处理后不要保存在
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
或加盐哈希,这样就没有人可以尝试更改该值来改变网站的行为.