在新的空DataTable上使用"using"是一个好的做法吗?

4

我一直在重写一些旧代码,使得对于我的DataTable,使用using语句代替记住每次都要Dispose:

using (DataTable dt = BLL.GetDataTable()) {
   foreach(DataRow dr in dt.Rows) {
     // iteration logic
   }
}

然而,在某些特定情况下,DataTable的内容会因为变量而有所不同,因此我首先创建初始的DataTable,然后再分配值:

DataTable dt = new DataTable();
switch(foo) {
  case bar:
     dt = BLL.GetDataTable(bar);
     break;
  default:
     dt = BLL.GetDataTable();
     break;
}
// iteration logic here
dt.Dispose();

将其改为使用using后,我有:

using (DataTable dt = new DataTable()) {
    switch(foo) {
      case bar:
         dt = BLL.GetDataTable(bar);
         break;
      default:
         dt = BLL.GetDataTable();
         break;
    }
    // iteration logic here
}

这种做法好吗(即使用 using 语句创建 empty DataTable)?我不知道为什么,但感觉不太对。


你最后的例子不应该编译通过,会出现以下错误:"无法分配给 'dt',因为它是一个 'using 变量'"。 - ProgrammingLlama
@John 我正在测试同样的问题(检查哪个dispose被调用)。确实,你不能分配在using中使用的变量。 - Jeroen van Langen
最佳实践是在所有可处理对象上使用using()。正如@John所述,您的第三个示例将无法工作。但是,您可以将您的情况分配给一个func<>并在using()中使用它。 - Rabban
谢谢大家。@Rabban,您能否为我提供一个 func<> 的例子以便我理解吗? - EvilDr
3个回答

5

正如我在评论中所述,你最后的例子行不通。如果你想要做这样的事情,可以将DataTable生成移入一个单独的函数中:

public DataTable GetBLLDataTable()
{
    switch(foo)
    {
        case bar:
            return BLL.GetDataTable(bar);
            break;
        default:
            return BLL.GetDataTable();
            break;
    }
}

然后在你的using语句中使用此方法返回的DataTable:

using (DataTable dt = GetBLLDataTable()) {
    // iteration logic here
}

那很有道理。谢谢。 - EvilDr

4
在你的最后一个示例中,你只处理了第一个DataTable对象,而没有处理被赋值的其他对象。
"using"语句只是"try/finally"的语法糖。你可以将你的最后一个示例改写为:
DataTable dt;
try
{
    switch (foo)
    {
        case bar:
            dt = BLL.GetDataTable(bar);
            break;
        default:
            dt = BLL.GetDataTable();
            break;
    }
}
finally
{
    dt?.Dispose();
}

这将确保您的 IDisposable 对象始终得到释放。然而,在这种情况下,这是一个有点奇怪的例子,因为我不明白为什么您会在 switch 中分配一个 DataTable 然后立即对其进行处理。


1
如果 BLL.GetDataTable(bar) 抛出异常 - dt 将为 null。并且 dt.Dispose();- 再抛出一个异常(“对象引用未设置为对象实例”) - Basil Kosovan
1
@Basil 但是,一旦 OP 调用它们的代码在 BLL 中生成数据表,你的代码也会遇到同样的问题。 - ProgrammingLlama
请问您能澄清一下您第一句话中的“...不是分配给其他变量”的意思吗?我原本以为,因为只有一个变量,调用该变量的Dispose方法就可以确保内存中没有剩余内容了。 - EvilDr
2
@EvilDr 想象一下 DataTable a = new DataTable(); a = new DataTable(); a.Dispose(); - 你创建了两个 DataTable 对象,但只释放了其中一个。使用 using 语句,.NET 可以防止你构建这种情况,就像我在评论中提到的那样 :) 请注意,取消分配对象并不会将其从内存中删除。.NET 最终会收集 托管 内存,但 Dispose 模式主要用于处理 非托管 资源,这些资源垃圾回收器不知道。 - ProgrammingLlama
嗯,非常有见地。我没有意识到未分配的对象不会立即自动删除。谢谢。 - EvilDr

2

这是另一种方法,与John所说的类似。您可以使用func<>设置您的get方法,并在using()中使用它。

"Original Answer"翻译成中文为"最初的回答"

Func<DataTable> func = null;
switch (foo)
{
    case bar:
        func = () => BLL.GetDataTable(bar);
        break;
    default:
        func = () => BLL.GetDataTable();
        break;
 }

 using (var dt = func())
 {
     // iteration logic here
 }

个人而言,我更喜欢约翰的方法,它更易读。但是它们都是一样的,所以你可以使用你最喜欢的那种。

最初的回答


2
现在我们只需要有人用本地方法回答 :-D - ProgrammingLlama
接下来的一个小时左右,我可能会在这里... https://dev59.com/7nA65IYBdhLWcg3wzx9l (!) - EvilDr
@John 不要挑战他们^^ - Rabban
@EvilDr 如果你不知道 func<> 是什么,没关系,只需使用此处发布的其他方法之一即可。我发布这个解决方案只是为了完整性。 - Rabban

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