C#条件使用块语句

49

我有以下的代码,但它看起来很别扭。如何更好地构造它?我需要让我的消费类去实现IDisposable接口并有条件地构建网络访问类,并在完成后进行释放吗?

    protected void ValidateExportDirectoryExists()
    {
        if (useNetworkAccess)
        {
            using (new Core.NetworkAccess(username, password, domain))
            {
                CheckExportDirectoryExists();
            }
        }
        else
        {
            CheckExportDirectoryExists();
        }
    }

4
为什么这尴尬呢?对我来说看起来非常简单明了。 - Joel Etherton
3
可能是由于CheckExportDirectoryExists()调用的重复导致的。 - BoltClock
7
如果那是你的代码中最尴尬的部分,你做得非常好。 - Nate
10个回答

90

有一种选择,虽然有点不好,但可以行得通,基于这样一个事实:C#编译器在调用Dispose仅当资源非空时才会调用:

protected void ValidateExportDirectoryExists()
{
    using (useNetworkAccess 
               ? new Core.NetworkAccess(username, password, domain)
               : null)
    {
        CheckExportDirectoryExists();
    }
}

另一种选择是编写一个静态方法,该方法返回null或NetworkAccess:
private Core.NetworkAccess CreateNetworkAccessIfNecessary()
{
    return useNetworkAccess
        ? new Core.NetworkAccess(username, password, domain)) : null;
}

然后:

protected void ValidateExportDirectoryExists()
{
    using (CreateNetworkAccessIfNecessary())
    {
        CheckExportDirectoryExists();
    }
}

再次强调,我仍然不确定我是否喜欢原始版本...这取决于您需要使用此模式的频率。


1
你的逻辑是反过来的。交换 nullnew 表达式的位置。 - cdhowie
2
我不知道在using语句中使用null的情况。 - wageoghe
+1 我实际上非常喜欢这个。之前没有考虑过在这种情况下使用“null”,但它实际上使得事情更加清晰,因为方法调用有一堆参数。 - julealgon
如果资源是一个结构体,会怎样? - Logerfo
@Logerfo:我没有具体的答案,但这是一个相当小众的边角情况... - Jon Skeet
显示剩余3条评论

15

使用using语句可以避免“finally”块,并且只应在使代码更加易于跟踪时使用。对于您的情况,我会编写以下代码。它可能不如其他版本简洁,但更为直观。

protected void ValidateExportDirectoryExists()
{
    Core.NetworkAccess access = useNetworkAccess ? new Core.NetworkAccess(username, password, domain) : null;    

    try
    {
        CheckExportDirectoryExists()
    }
    finally
    {
       if (access != null)
       {
           access.Dispose();
       }
    }
}

我同意,我认为这比使用语句更能表达意图 - 因为由于 finally 语句中的 null 检查,立即明显地表明访问是可选的,而 using 语句则稍微掩盖了意图。 - Bittercoder
我认为这是最好的答案,因为它在做必要的事情时展示了意图,而且没有以任何方式“聪明地”处理。在我看来,这将是符合惯用法的 C# 方式。 - Mike Corcoran
你正在try/finally之外初始化你的可释放资源。这显然不是一个好主意。 - rold2007
如果对象的构造函数失败,您将无法处理它。这是来自Microsoft网站的链接,介绍了using语句在幕后的实现方式:https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/statements#the-using-statement - Dmitry S.

6
如果您在许多方法中重复此模式,您可以打破这种模式。
protected void OptionalNetworkCall(Action action)
{
    if (useNetworkAccess)
    {
        using (new Core.NetworkAccess(username, password, domain))
        {
            action();
        }
    }
    else
    {
        action();
    }
}

protected void ValidateExportDirectoryExists()
{
    OptionalNetworkCall(CheckExportDirectoryExists);
}

1
这很可能正是 OP 想要避免的。 - Dmitri Nesteruk

2
protected void ValidateExportDirectoryExists()
{
      var access = useNetworkAccess
          ? new Core.NetworkAccess(username, password, domain)
            : null;

      using (access)
      {
          CheckExportDirectoryExists();
      }
}

1

我不知道是否“更好”,但您可以使用空对象模式,并拥有一个“null”可处理的网络访问对象。就像这样:

protected void ValidateExportDirectoryExists()     
{
  using (GetNetworkAccess(username, password, domain))
  {                 
    CheckExportDirectoryExists();
  }
} 

protected IDisposable GetNetworkAccess(string username, string password, string domain)
{
  return useNetworkAccess ? new Core.NetworkAccess(username, password, domain) : new NullNetworkAccess(username, password, domain);
}

internal class NullNetworkAccess : IDisposable
{
  internal NullNetworkAccess(string username, string password, string domain)
  {
  }

  public void Dispose()
  {
  }
}

这可能太可爱了,以至于它自己都不好。

[编辑] 刚看到Jon的回答中提到null可以在using语句中使用。我之前不知道!


0

使用作用域仅在类实现IDisposable接口时才会处理对象,因此您需要实现dispose方法。


0

我想这真的只是一个外观问题,如果代码像那样简单的话。

我可以想象它可能会有另一种样子,而我的投票将是给你现在拥有的这个版本。


0

使用using语句包含的任何内容都将根据IDisposable接口调用其IDispoable.Dispose。如MSDN中所示,提供了using的方便语法...

提供了一种方便的语法,确保正确使用IDisposable对象。

因此,如果您在using语句中放置自定义类型,则应通过IDisposable接口适当清除其资源。


0
通过让你的类实现IDisposable接口,dispose方法只有在使用"using"语句时才会被调用。否则,你必须显式地调用dispose方法。
通常,IDisposable接口由管理垃圾回收器之外内存消耗的对象实现(例如使用非托管代码)。它提供了一种清理任何已使用内存的方式。
只要你的NetworkAccess类实现了IDisposable接口,当using语句的作用域完成时,dispose方法就会被调用。如果是托管代码,则不需要手动释放。让垃圾回收器自己工作即可。

0

使用自己的try/finally块,执行类似于'using'的逻辑,但仅在设置了useNetworkAccess时才进行dispose。请注意,如果useNetworkAccess可能会受到其他线程的影响,您应该复制其值并将该副本用于创建资源和处理它。


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