在C#中嵌套使用语句

SBu*_*ris 297 .net c# using file

我正在做一个项目.我必须比较两个文件的内容,看看它们是否精确匹配.

在进行大量错误检查和验证之前,我的初稿是:

  DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\TestArea\\");
  FileInfo[] files = di.GetFiles(filename + ".*");

  FileInfo outputFile = files.Where(f => f.Extension == ".out").Single<FileInfo>();
  FileInfo expectedFile = files.Where(f => f.Extension == ".exp").Single <FileInfo>();

  using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
  {
    using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
    {
      while (!(outFile.EndOfStream || expFile.EndOfStream))
      {
        if (outFile.ReadLine() != expFile.ReadLine())
        {
          return false;
        }
      }
      return (outFile.EndOfStream && expFile.EndOfStream);
    }
  }
Run Code Online (Sandbox Code Playgroud)

有嵌套using语句似乎有点奇怪.

有一个更好的方法吗?

SLa*_*aks 532

执行此操作的首选方法是仅{在最后一个using语句后放置一个左括号,如下所示:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
{
    ///...
}
Run Code Online (Sandbox Code Playgroud)

  • 不确定我是否发现更具可读性.如果有什么它打破了嵌套代码的外观.并且看起来第一个using语句是空的并且未使用.但是,我想有什么工作......:/ (37认同)
  • 清洁器?并且也不会强迫您使用相同的类型.即使类型与可读性和一致性相匹配,我总是这样做. (10认同)
  • @Bryan Watts,"反对者"可能表达了真正的偏好.一个不同的开发团队很可能会在建议的情况下提出异议.唯一可以知道的方法是在并行宇宙中再次运行实验. (10认同)
  • @Hardryv:Visual Studio的自动格式将其删除.这个想法看起来像一个变量声明列表. (7认同)
  • @fmuecke:那不是真的; 它会工作."IDisposable"状态的规则是两次调用`Dispose()`应该什么都不做.这个规则只适用于写得不好的一次性用品. (5认同)
  • 即使你要修剪括号,为了约定我建议你在嵌套部分保留缩进 (3认同)
  • CAVEAT:将流对象传递给StreamReader时,这不起作用,因为StreamReader也会处理流!请参阅http://msdn.microsoft.com/de-de/library/ms182334.aspx (2认同)
  • @SLaks耶稣基督。是的,我知道.. 反问句指出您不能使用单个语句代替任何旧块(正如您所说)。让我重新表述一下 - 我们是否认为多个 using 语句**应该**按照 Microsoft C# 团队中某人的设计决策这样编写?Visual Studio 的不同嵌套方法表明这是真的。 (2认同)

Gav*_*n H 135

如果对象属于同一类型,则可以执行以下操作

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
                    expFile = new StreamReader(expectedFile.OpenRead()))
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 确实有效的@jpierson,但是当你从using块中调用`IDisposable`对象时,我们不能调用任何类成员(没有强制转换,这会失败点imo). (8认同)

jas*_*son 33

IDisposables是相同类型时,您可以执行以下操作:

 using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
     expFile = new StreamReader(expectedFile.OpenRead()) {
     // ...
 }
Run Code Online (Sandbox Code Playgroud)

MSDN页面上using有关于此语言功能的文档.

无论IDisposables是否属于同一类型,您都可以执行以下操作:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamWriter anotherFile = new StreamReader(anotherFile.OpenRead()))
{ 
     // ...
}
Run Code Online (Sandbox Code Playgroud)


Bot*_*000 17

如果你不介意在using块之前声明你的using块的变量,你可以在同一个using语句中声明它们.

    Test t; 
    Blah u;
    using (IDisposable x = (t = new Test()), y = (u = new Blah())) {
        // whatever...
    }
Run Code Online (Sandbox Code Playgroud)

这样,x和y只是IDisposable类型的占位符变量,供使用的块使用,并且在代码中使用t和u.我以为我会提到.

  • 我觉得这会让新开发人员在查看您的代码时感到困惑。 (5认同)
  • 这可能是一个坏习惯。这样做的副作用是,即使释放了非托管资源,这些变量仍将存在。根据Microsoft的C#参考,“您可以实例化资源对象,然后将变量传递给using语句,但这不是最佳实践。在这种情况下,即使控件将保留using块,该对象仍会保留在作用域中可能再也无法访问其非托管资源。” (4认同)

小智 11

C# 8.0 开始,您可以使用using 声明

using var outFile = new StreamReader(outputFile.OpenRead());
using var expFile = new StreamReader(expectedFile.OpenRead());
while (!(outFile.EndOfStream || expFile.EndOfStream))
{
    if (outFile.ReadLine() != expFile.ReadLine())
    {
         return false;
    }
}
return (outFile.EndOfStream && expFile.EndOfStream);
Run Code Online (Sandbox Code Playgroud)

这将在变量范围的末尾处理 using 变量,即在方法的末尾。


Jas*_*ams 9

如果要有效地比较文件,请不要使用StreamReader,然后不需要使用 - 您可以使用低级流读取来提取数据缓冲区以进行比较.

您还可以首先比较文件大小等内容,以快速检测不同的文件,以节省您自己必须读取的所有数据.


jpi*_*son 8

using语句使用IDisposable接口,因此另一个选项可能是创建某种类型的实现IDisposable的复合类,并且引用了通常放在using语句中的所有IDisposable对象.这样做的缺点是你必须首先在范围之外声明你的变量,以便它们在需要比其他一些建议所需的更多代码行的使用块中更有用.

Connection c = new ...; 
Transaction t = new ...;

using (new DisposableCollection(c, t))
{
   ...
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,DisposableCollection的构造函数是一个params数组,因此您可以根据需要输入.


aqu*_*nas 7

你也可以说:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
   ...
}
Run Code Online (Sandbox Code Playgroud)

但有些人可能会发现难以阅读.顺便说一句,作为您问题的优化,为什么在逐行前检查文件大小是否相同?


cod*_*nix 6

您可以使用逗号将多个一次性对象组合在一个using语句中:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
       expFile = new StreamReader(expectedFile.OpenRead()))
{

}
Run Code Online (Sandbox Code Playgroud)


yoy*_*sef 6

您可以省略除最内层以外的所有括号:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
  while (!(outFile.EndOfStream || expFile.EndOfStream))
  {
    if (outFile.ReadLine() != expFile.ReadLine())
    {
      return false;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我认为这比使用其他人所建议的几种相同类型更干净,但我相信很多人会认为这是令人困惑的


SLa*_*aks 6

另外,如果您已经知道路径,则无需扫描目录。

相反,我会推荐这样的东西:

string directory = Path.Combine(Environment.CurrentDirectory, @"TestArea\");

using (StreamReader outFile = File.OpenText(directory + filename + ".out"))
using (StreamReader expFile = File.OpenText(directory + filename + ".exp")) 
{
    //...
Run Code Online (Sandbox Code Playgroud)

Path.Combine将向路径添加文件夹或文件名,并确保路径和名称之间正好有一个反斜杠。

File.OpenText将打开一个文件并StreamReader一次性创建一个。

通过在字符串前添加 @ 前缀,您可以避免转义每个反斜杠(例如,@"a\b\c"


wom*_*omp 5

它没什么奇怪的. using是代码块完成后确保处理对象的简便方法.如果您的外部块中有一个一次性物体,内部块需要使用,这是完全可以接受的.

编辑:输入太慢以显示合并代码示例.+1给其他人.


Cha*_*ana 5

在这种情况下,为了增加清晰度,因为每个连续语句都是单个语句(而不是块),所以可以省略所有括号:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
  using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
    while (!(outFile.EndOfStream || expFile.EndOfStream))  
       if (outFile.ReadLine() != expFile.ReadLine())    
          return false;  
Run Code Online (Sandbox Code Playgroud)