我能否将using() {}块与方法的out参数结合使用?

12

给定一个方法

public static bool Connection.TryCreate(out Connection connection) {}

还有一段调用代码:

Connection connection;
if (!Connection.TryCreate(out connection))
    // handle failure gracefully.

/*
 * work with connection
 *
 * …
 *
 */

connection.Dispose();

我正在使用与bool.TryParse等相同的模式,即TryCreate返回操作是否成功。

我意识到using()变量在其块内需要是只读的,但是否有一种方法可以将上述代码转换为using() {}块(TryCreate仅设置一次), 类似这样:

using (Connection connection)
{
    if (!Connection.TryCreate(out connection))
        // this would leave the using() block prematurely

    /*
     * work with sconnection
     *
     * …
     *
     */
}

(这段代码无法编译:

error CS1657: 无法将“connection”作为ref或out参数传递,因为它是一个“using变量”

)


你已经对connection进行了空/非空值的简单测试了吗?它是否编译通过?是否运行成功? - Uwe Keim
是的,抱歉,我应该提到我的虚构例子无法编译。我已经相应地修改了帖子。 - Sören Kuklau
5个回答

8
不,那是不可能的。 using (x) {...} 结构在进入块时会复制 x,因此您可以这样做:
var x = new FileStream(...);
using (x)
{
    x = null;
}

using块结束时,流仍将被处理。

推论是这也不起作用:

Stream x = null;
using (x)
{
    x = new FileStream(...);
}

在 using 块内构造的流将不会被处理。
然而,你可以这样做:
Connection connection;
if (Connection.TryCreate(out connection))
    using (connection)
    {
    }

在C# 7.0及以上版本中,您可以将此与“out变量”结合使用,形成以下内容:
if (Connection.TryCreate(out var connection))
    using (connection)
    {
    }

4

看起来是 Try* 模式的不良使用(有人认为这是一个反模式)。

与其使用 TryCreate,不如只提供一个 Create 方法,如果操作失败则抛出异常,并返回已创建的连接。

这样,您就可以照常进行:

using(Connection connection = Connection.Create())
{
}

或者,如果您想避免抛出异常并需要 try{}catch{},也可以在无法创建连接时使 Create 方法返回 null 并进行测试。


我指的是一个常规的返回值,在成功时为非空,否则为null。 - Marc Gravell
@MarcGravell - 完全同意 - 返回 null 是一个很好的替代方案。 - Oded
@Oded - 我在写错误答案时假设它是这样的 - 这是因为在我的看法中,在创建操作中使用Parse中的Try*是没有意义的。 - Hogan
@Hogan - 我同意,这就是为什么我的答案避免使用Try方法的原因。 - Oded
拥有一个“try”方法的工厂是完全合理的。然而,常见的模式是将“try”方法返回创建的对象作为输出引用参数,这似乎更加可疑。一个接口IVehicleFactory<T>定义了一个方法bool TryBuildCarToSpecs(CarSpects theSpecs, out T result),只能被想要构建特定类型T的代码使用。相比之下,如果函数是例如T TryBuildCarToSpecs(CarSpecs theSpecs, ref BuildProblemReport faults),那么接口可以被声明为协变的,并且想要一个IVehicleFactory<Car>的代码可以... - supercat
显示剩余5条评论

3
你可以像这样做:
Connection connection;
if (Connection.TryCreate(out connection))
{
    using (connection)
    {
        …
    }
}

但是如果失败了,最好返回null

using (Connection connection = Connection.Create())
{
    if (connection != null)
    {
        …
    }
}
< p > 由 using 创建的 finally 块会检查 connection 是否为 null,如果是,则不执行任何操作。

另外,如果在 using 中不声明变量,则该变量不必为只读。


1
从MSDN - using语句您可以实例化资源对象,然后将变量传递给using语句,但这不是最佳实践。 - Oded
是的,但我认为这是最好的解决方案,如果你不想出于某种原因修改TryCreate() - svick
当TryCreate失败时会发生什么? - Hogan
这个编译通过了,但它真的保证在所有情况下资源都被清理干净了吗? - Sören Kuklau
我会接受答案 - 就第一部分而言。第二部分似乎有问题,如果连接最终确实为“null”,代码将悄无声息地什么也不做,而且我甚至无法检查“null”,因为该块永远不会被执行。 - Sören Kuklau
显示剩余4条评论

1

不需要。如果您担心在方法调用和使用之间出现异常,可以使用try/finally:

Connection conn = null;
try {
    if(!conn.TryCreate(out conn)) return;
    ...
} finally {
    if(conn != null) conn.Dispose();
}

当然可以,但那基本上是重新实现了 using 的语法糖。 :) 我正在尝试避免这种情况。 - Sören Kuklau

0

向侧面迈进?

public class ConnectTo : IDisposable
{

  public Connection CurrentConnection {get; private set;}

  public ConnectTo()
  {
    CurrentConnection = null;
    // Connect up to whatever.
  }


  #region IDisposable

  // Blah blah

  #endregion
}

那么

using( ConnectedTo conn = new ConnectTo())
{
  if (conn.CurrentConnection != null)
  {
    //Do Stuff
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接