出于方便和安全的原因,我想使用using
语句来分配和释放来自/到池的对象
public class Resource : IDisposable
{
public void Dispose()
{
ResourcePool.ReleaseResource(this);
}
}
public class ResourcePool
{
static Stack<Resource> pool = new Stack<Resource>();
public static Resource GetResource()
{
return pool.Pop();
}
public static void ReleaseResource(Resource r)
{
pool.Push(r);
}
}
和访问池一样
using (Resource r = ResourcePool.GetResource())
{
r.DoSomething();
}
我找到了一些关于滥用using
和Dispose()
进行范围处理的主题,但所有主题都包含using (Blah b = _NEW_ Blah())
。
这里的对象在离开使用范围后不会被释放,而是保留在池中
如果using语句只是扩展为普通try finally Dispose()
,那么这应该可以正常工作但是在幕后会发生更多的事情,或者在未来的.Net版本中这种情况不会发生吗?
答案 0 :(得分:7)
这根本不是滥用 - 这是C#的常见范围处理习惯用语。例如,ADO.NET对象(连接,语句,查询结果)通常包含在using
块中,即使其中一些对象在其Dispose
方法中被释放回其池中:
using (var conn = new SqlConnection(dbConnectionString)) {
// conn is visible inside this scope
...
} // conn gets released back to its connection pool
答案 1 :(得分:2)
这是使用IDisposable
的有效方式。
实际上,这也是在.NET中完成连接池的方式 - 在using
语句中包装DBConnection
对象以确保连接关闭并返回到连接池。
TransactionScope
是使用Dispose
模式回滚未完成事务的类的另一个示例:
对Dispose方法的调用标志着事务范围的结束。
答案 2 :(得分:2)
如果using语句只是简单地扩展到一个普通的尝试,最后Dispose()这应该可以正常工作但是在幕后会发生更多的事情,或者在将来的.Net版本中这种情况不会发生吗?
确实如此。您的代码应该可以正常工作,并且由规范保证以相同的方式继续工作。实际上,这是相当常见的(在SQL中查看连接池,这是一个很好的例子。)
您编写的代码的主要问题是,您可以在ReleaseResource
内明确调用using
,这可能会导致池被多次推送资源,因为它是公共API。
答案 3 :(得分:2)
这看起来像是对IDisposable
的滥用以及对我的糟糕设计决定。首先,它强制存储在池中的对象了解池。这类似于创建一个List类型,该类型强制其中的对象实现特定接口或从某个特定类派生。就像LinkedList类一样,它强制数据项包含列表可以使用的Next
和Previous
指针。
此外,您让池为您分配资源,但是资源有一个调用将自己放回池中。这看起来很奇怪。
我认为更好的选择是:
var resource = ResourcePool.GetResource();
try
{
}
finally
{
ResourcePool.FreeResource(resource);
}
这是一个更多的代码(尝试/最终而不是using
),但更简洁的设计。它可以减轻所包含的对象对容器的了解,并且更清楚地表明池正在管理对象。
答案 4 :(得分:1)
您对using
声明的理解是正确的(try
,finally
,Dispose
)。我不认为这种变化很快就会发生。如果有的话会有很多事情发生。
您计划的内容并不一定有任何问题。我以前见过这种事情,Dispose
实际上没有关闭对象,而是把它放到某种不完全可操作的状态。
如果您对此感到担忧,可以通过遵循通常的Dispose
实现模式的方式实现此目的。只需要一个实现IDisposable
的包装类,并公开底层类的所有方法。处理包装器时,将底层对象放入池中,而不是包装器。然后你可以考虑关闭包装器,虽然它包装的东西不是。
答案 5 :(得分:0)
你在做什么就像C ++和RAII。在C#中,它与您可以得到的C ++ / RAII习惯用语非常接近。
知道关于C#的事情或者两件事的Eric Lippert坚决反对使用IDispose和使用语句作为C#RAII习语。请在此处查看他的深度回复Is it abusive to use IDisposable and "using" as a means for getting "scoped behavior" for exception safety?。此RAII方式中使用的IDisposable的部分问题是IDisposable具有非常严格的要求才能正确使用。几乎所有使用IDisposable的C#代码都无法正确实现模式。 Joe Duffy撰写了一篇博文,详细介绍了实现IDisposable模式http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/的正确方法。 Joe的信息比MSDN上提到的更加详细和广泛。 Joe也知道关于C#的一件事或两件事,并且有很多非常聪明的贡献者帮助充实了这个文档。
可以通过简单的方法来实现简单的最小IDisposable模式(例如在RAII中使用),例如密封类,并且因为没有没有终结器的非托管资源等等。 MSDN https://msdn.microsoft.com/en-us/library/system.objectdisposedexception%28v=vs.110%29.aspx是一个很好的概述,但Joe的信息包含了所有的血腥细节。
但是你无法摆脱IDisposable的一件事就是它的病毒&#34;性质。保留IDisisable成员的类本身应该成为IDisposable ......在using(RAII raii = Pool.GetRAII())
场景中不是问题,但需要注意的事项。
所有这一切,尽管埃里克的职位(我倾向于在大多数其他事情上与他达成一致),以及乔的50篇关于如何正确实施IDisposable模式的文章......我自己用它作为C#/ RAII的习语。
现在只有当C#有1个不可空的引用(比如C ++或D或Spec#)和2)深度不可变的数据类型(比如D,甚至F#[你可以做F#类的不可变的C#,但它有很多样板,它太难以正确......使得很难,而难以理解 ] 3)按合同设计作为语言本身的一部分(如D或Eiffel或Spec#,而不是C#Code Contracts憎恶)。 叹气也许C#7。