如何 return 一个依赖于一系列 using 语句的对象?
How to return an object that's dependent on a chain of using statements?
我想写一个类似这个的方法:
C Make()
{
using (var a = new A())
using (var b = new B(a))
{
return new C(b);
}
}
这很糟糕,因为当方法 returns、c
保留对已处置对象的引用时。
注意:
A
实施 IDisposable
。
B
实施 IDisposable
.
C
确实
不实施 IDisposable
因为 C
的作者声明 C
不取得 b
. 的所有权
这是说明文档很重要的一个很好的例子。它构成了 class.
合同的一部分
你当然已经意识到了这一点,既然你说了,
the author of C
stated that C
does not take ownership of b
.
这意味着您无法在此处完全实现您的目标。您所拥有的可能是不正确的,因为 a
和 b
将在返回新的 C
之前立即处理掉。
您需要稍微重构此代码。要么更改 Make()
使其接受类型为 B
的参数;调用者将对 B
和 C
的生命周期负责。或者编写一个新的 class 来实现 IDisposable
并包装 A
、B
和 C
并通过 属性 公开 C
。
如果您拥有类型 C
,您可以考虑修改它以允许它有选择地取得 b
的所有权。这是 .NET 本身中相当常见的模式。参见,例如,XmlReaderSettings.CloseInput.
This is bad since I cant reference a
and b
from outside and dispose of them.
只有当您保留对 a
或 b
的引用时,这才是糟糕的,它们可以从您的代码外部处理。然而,这些对象是一次性的,并且因为 C
没有创建它们或转移所有权,所以它应该在完成构造函数之前从 A
或 B
获取它需要的任何东西,而不是保留对一次性对象的引用:
class C {
private readonly string x;
private readonly int y;
public C(B b) {
// Using b here is OK
x = b.X;
y = b.Y;
// We are done with b, so when b is disposed, C will not break
}
}
Unfortunately, C
keeps a reference to it's b
throughout its lifetime and expects the caller to dispose of it when C
is no longer needed
如果您无法控制 C
,请为其制作一个 IDisposable
包装器,取得 B
的所有权,并在 C
不再存在时将其处置必要的:
class WrapC : IDisposable {
private readonly B b;
public C C { get; private set; }
public WrapC (B b) {
this.b = b;
C = new C(b);
}
public void Dispose() {
b.Dispose();
}
}
删除 B
的 using
语句,并在完成后处理 WrapC
。
你的情况和我在查询数据库时有时看到的很相似。为了分离逻辑,您有时会看到这样的代码:
var reader = ExecuteSQL("SELECT ...");
while (reader.Read()) // <-- this fails, because the connection is closed.
{
// process row...
}
public SqlDataReader ExecuteSQL(string sql)
{
using (SqlConnection conn = new SqlConnection("..."))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
return cmd.ExecuteReader();
}
}
}
但是,当然,这行不通,因为到 ExecuteSQL
方法的 SqlDataReader
returns 时,连接已经关闭(处置)。
因此,上述方法的替代方法是使用委托来实现关注点分离,但仍然允许代码正常工作:
ExecuteSQL(reader => {
// write code that reads from the SqlDataReader here.
});
public void ExecuteSQL(string sql, Action<SqlDataReader> processRow)
{
using (SqlConnection conn = new SqlConnection("..."))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
processRow(reader);
}
}
}
}
}
那么,也许您可以尝试类似的方法?像这样:
MakeAndProcess(c => {
// write code that uses C here.
// This will work, because A and B are not disposed yet.
});
public void MakeAndProcess(Action<C> processC)
{
using (var a = new A())
using (var b = new B(a))
{
processC(new C(b));
}
}
我想写一个类似这个的方法:
C Make()
{
using (var a = new A())
using (var b = new B(a))
{
return new C(b);
}
}
这很糟糕,因为当方法 returns、c
保留对已处置对象的引用时。
注意:
A
实施IDisposable
。B
实施IDisposable
.C
确实 不实施IDisposable
因为C
的作者声明C
不取得b
. 的所有权
这是说明文档很重要的一个很好的例子。它构成了 class.
合同的一部分你当然已经意识到了这一点,既然你说了,
the author of
C
stated thatC
does not take ownership ofb
.
这意味着您无法在此处完全实现您的目标。您所拥有的可能是不正确的,因为 a
和 b
将在返回新的 C
之前立即处理掉。
您需要稍微重构此代码。要么更改 Make()
使其接受类型为 B
的参数;调用者将对 B
和 C
的生命周期负责。或者编写一个新的 class 来实现 IDisposable
并包装 A
、B
和 C
并通过 属性 公开 C
。
如果您拥有类型 C
,您可以考虑修改它以允许它有选择地取得 b
的所有权。这是 .NET 本身中相当常见的模式。参见,例如,XmlReaderSettings.CloseInput.
This is bad since I cant reference
a
andb
from outside and dispose of them.
只有当您保留对 a
或 b
的引用时,这才是糟糕的,它们可以从您的代码外部处理。然而,这些对象是一次性的,并且因为 C
没有创建它们或转移所有权,所以它应该在完成构造函数之前从 A
或 B
获取它需要的任何东西,而不是保留对一次性对象的引用:
class C {
private readonly string x;
private readonly int y;
public C(B b) {
// Using b here is OK
x = b.X;
y = b.Y;
// We are done with b, so when b is disposed, C will not break
}
}
Unfortunately,
C
keeps a reference to it'sb
throughout its lifetime and expects the caller to dispose of it whenC
is no longer needed
如果您无法控制 C
,请为其制作一个 IDisposable
包装器,取得 B
的所有权,并在 C
不再存在时将其处置必要的:
class WrapC : IDisposable {
private readonly B b;
public C C { get; private set; }
public WrapC (B b) {
this.b = b;
C = new C(b);
}
public void Dispose() {
b.Dispose();
}
}
删除 B
的 using
语句,并在完成后处理 WrapC
。
你的情况和我在查询数据库时有时看到的很相似。为了分离逻辑,您有时会看到这样的代码:
var reader = ExecuteSQL("SELECT ...");
while (reader.Read()) // <-- this fails, because the connection is closed.
{
// process row...
}
public SqlDataReader ExecuteSQL(string sql)
{
using (SqlConnection conn = new SqlConnection("..."))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
return cmd.ExecuteReader();
}
}
}
但是,当然,这行不通,因为到 ExecuteSQL
方法的 SqlDataReader
returns 时,连接已经关闭(处置)。
因此,上述方法的替代方法是使用委托来实现关注点分离,但仍然允许代码正常工作:
ExecuteSQL(reader => {
// write code that reads from the SqlDataReader here.
});
public void ExecuteSQL(string sql, Action<SqlDataReader> processRow)
{
using (SqlConnection conn = new SqlConnection("..."))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
processRow(reader);
}
}
}
}
}
那么,也许您可以尝试类似的方法?像这样:
MakeAndProcess(c => {
// write code that uses C here.
// This will work, because A and B are not disposed yet.
});
public void MakeAndProcess(Action<C> processC)
{
using (var a = new A())
using (var b = new B(a))
{
processC(new C(b));
}
}