2009-08-25 2 views
223

Я работаю над проектом и должен сравнить два файла и посмотреть, соответствуют ли они друг другу.Вложенные операторы using в C#

Мой первый проект, прежде чем много проверки ошибок и проверки придумал:

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); 
    } 
    } 

Это кажется немного странным, гнездились с помощью инструкций.

Есть ли лучший способ сделать это?

+2

, если один из файлов короче другого, но соответствует всем его байтам с большим файлом, указанный выше код вернет true, хотя файлы не являются точным совпадением –

+0

@Rune FS: Хорошая уловка, исправлено – SBurris

+0

Я думаю, что я, возможно, нашел синтаксически более чистый способ объявить эту инструкцию using, и, похоже, она работает для меня? использование var в качестве вашего типа в операторе using вместо IDisposable, похоже, позволяет мне создавать экземпляры обоих моих объектов и вызывать их свойства и методы класса, которому они распределены, как при использовании (var uow = UnitOfWorkType1(), uow2 = UnitOfWorkType2()) {} – Caleb

ответ

418

предпочтительным способом сделать это является то, что нужно только положить открытую скобу { после последнего using, например:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    ///... 
} 
+0

Ничего себе, сегодня я чему-то научился. Вы можете использовать этот подход с разным типом. +1 –

+0

Да, этот подход хорош, когда типы разные. –

+9

Очиститель? а также не заставляет вас использовать одни и те же типы. Я всегда делаю это так, даже если типы соответствуют читабельности и последовательности. – meandmycode

104

Если объекты имеют же типа вы можете сделать следующее

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()), 
        expFile = new StreamReader(expectedFile.OpenRead())) 
{ 
    // ... 
} 
+1

Ну, они все одинаковы, если они все IDisposable, возможно, бросок будет работать? – jpierson

+4

@jpierson, который работает, да, но тогда, когда вы вызываете объекты IDisposable изнутри блока использования, мы не можем вызывать ни одного из членов класса (без приведения, который побеждает точку imo). – Connell

+0

IDisposable - это тип, поэтому просто используйте его как тип, чтобы иметь список смешанных типов, как видно из нескольких других ответов. –

4

Там нет ничего странного об этом. using - это сокращенный способ обеспечения удаления объекта после завершения кода. Если в вашем внешнем блоке есть одноразовый объект, который должен использовать внутренний блок, это вполне приемлемо.

Редактировать: слишком медленно набирать текст, чтобы отобразить пример сводного кода. +1 всем.

23

Когда IDisposable s одного и того же типа, вы можете сделать следующее:

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

На странице MSDN на using содержит документацию по этой функции языка.

Вы можете сделать следующее или нет IDisposable с одного и того же типа:

using (StreamReader outFile = new StreamReader(outputFile.OpenRead())) 
using (StreamWriter anotherFile = new StreamReader(anotherFile.OpenRead())) 
{ 
    // ... 
} 
3

Вы можете сгруппировать несколько одноразовых объектов в одном, используя-заявление с запятыми:

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

} 
2

Это время, когда я кодирую код. Вы могли бы подумать переместить второй оператор using в другую функцию?

4

Вы можете опустить скобки на всех, кроме самого внутреннего использования:

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; 
    } 
    } 
} 

Я думаю, что это гораздо чище, чем положить несколько одного и того же типа в том же использовании, как и другие предложили, но я м, что многие люди будут думать, что это сбивает с толку

+3

Вы можете опустить все скобки в этом случае ... –

6

можно также сказать:

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

Но некоторые люди могли бы найти, что трудно читать. BTW, как оптимизация вашей проблемы, почему вы не проверяете, что размеры файлов имеют одинаковый размер, прежде чем идти по очереди?

2

Кроме того, если вы уже знаете пути, нет смысла сканировать каталог.

Вместо этого я бы рекомендовал что-то вроде этого:

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

using (StreamReader outFile = File.OpenText(directory + filename + ".out")) 
using (StreamReader expFile = File.OpenText(directory + filename + ".exp"))) 
{ 
    //... 

Path.Combine добавит папку или имя файла, путь и убедитесь, что имеется ровно один обратный слэш между траекторией и именем.

File.OpenText откроет файл и создаст StreamReader за один раз.

предваряя строку с @, вы можете избежать того, чтобы избежать каждой обратной косой черты (например, @"a\b\c")

8

Если вы хотите, чтобы эффективно сравнивать файлы, не используйте StreamReaders на всех, а затем usings Арен Не обязательно - вы можете использовать чтение низкого уровня, чтобы вытащить буферы данных для сравнения.

Вы также можете сначала сравнить такие вещи, как размер файла, чтобы быстро обнаружить разные файлы, чтобы вы сами могли прочитать все данные.

+0

Да, проверка размера файла - хорошая идея, экономит ваше время или читает все байты. (+1) – TimothyP

2

Вы также спрашиваете, есть ли лучший способ сравнить с файлами? Я предпочитаю вычислять CRC или MD5 для обоих файлов и сравнивать их.

Например, вы можете использовать следующий метод расширения:

public static class ByteArrayExtender 
    { 
     static ushort[] CRC16_TABLE = { 
         0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241, 
         0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440, 
         0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40, 
         0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841, 
         0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40, 
         0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41, 
         0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641, 
         0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040, 
         0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240, 
         0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441, 
         0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41, 
         0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840, 
         0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41, 
         0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40, 
         0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640, 
         0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041, 
         0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240, 
         0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441, 
         0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41, 
         0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840, 
         0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41, 
         0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40, 
         0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640, 
         0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041, 
         0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241, 
         0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440, 
         0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40, 
         0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841, 
         0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40, 
         0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41, 
         0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641, 
         0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 }; 


     public static ushort CalculateCRC16(this byte[] source) 
     { 
      ushort crc = 0; 

      for (int i = 0; i < source.Length; i++) 
      { 
       crc = (ushort)((crc >> 8)^CRC16_TABLE[(crc^(ushort)source[i]) & 0xFF]); 
      } 

      return crc; 
     } 

После того, как вы сделали, что это довольно легко сравнить файлы:

public bool filesAreEqual(string outFile, string expFile) 
{ 
    var outFileBytes = File.ReadAllBytes(outFile); 
    var expFileBytes = File.ReadAllBytes(expFile); 

    return (outFileBytes.CalculateCRC16() == expFileBytes.CalculateCRC16()); 
} 

Вы можете использовать встроенный в System.Security .Cryptography.MD5 class, , но вычисленный хэш является байтом [], поэтому вам все равно придется сравнивать эти два массива.

+2

Вместо того, чтобы принимать байтовый массив, метод должен взять объект «Стрим» и вызвать метод «ReadByte» до тех пор, пока он не вернет -1. Это позволит сэкономить большие объемы памяти для больших файлов. – SLaks

+0

Как бы вы вычислили crc по всем байтам? – TimothyP

+0

О, неважно, что я сказал: p Thnx, я изменю это в своем коде: p Мы используем его только для данных <1000 байт, так что еще не заметили проблем, но все равно изменимся – TimothyP

4

И просто добавить к ясности, в данном случае, так как каждое последующее утверждение одного оператора, (а не блок), вы можете опустить все скобки:

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; 
+0

Интересное решение; делая это/даже используя 1 набор кронштейнов на самом низком уровне, возможно, выполняет ту же задачу, что и укладка их по левому краю (более чистое ИМО), при этом обращаясь к желанию косметического гнездования, о котором упоминалось другое, чтобы показать любое подчинение. – user1172173

13

, если вы не» t mind, объявляя переменные для вашего блока использования перед блоком использования, вы можете объявить их все в одном и том же операторе использования.

Test t; 
    Blah u; 
    using (IDisposable x = (t = new Test()), y = (u = new Blah())) { 
     // whatever... 
    } 

Таким образом, х и у являются просто заполнитель переменного типа IDisposable для блока, используя для использования, и вы используете т и и внутри ваш код. Просто подумал, что я бы сказал.

+0

Мне кажется, что это смущает нового разработчика, который смотрит на ваш код. – Zack

+0

Это может быть плохая практика; он имеет побочный эффект, что переменные все еще будут существовать даже после освобождения неуправляемых ресурсов. Согласно ссылке Microsoft C#: «Вы можете создать экземпляр объекта ресурса, а затем передать переменную в оператор using, но это не самая лучшая практика. В этом случае объект остается в области действия после того, как элемент управления покидает блок использования, даже если он будет вероятно, больше не имеют доступа к своим неуправляемым ресурсам ». –

+0

@RobertAltman Вы правы, и в реальном коде я бы использовал другой подход (возможно, тот, который был у Gavin H). Это всего лишь менее предпочтительная альтернатива. – Botz3000

7

Оператор using работает с интерфейсом IDisposable, поэтому другой вариант может заключаться в создании некоторого типа составного класса, который реализует IDisposable и имеет ссылки на все объекты IDisposable, которые вы обычно помещаете в свой оператор using. Нижняя сторона этого заключается в том, что вы должны объявить свои переменные сначала и вне области, чтобы они могли быть полезны в блоке использования, требующем больше строк кода, чем потребуются некоторые другие предложения.

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

using (new DisposableCollection(c, t)) 
{ 
    ... 
} 

Конструктор DisposableCollection является массив PARAMS в этом случае, так что вы можете кормить, как много, как вам нравится.

2

Я думаю, что я нашел синтаксически более чистый способ объявить эту инструкцию using, и, похоже, она работает для меня?используя var, поскольку ваш тип в операторе using вместо IDisposable, как представляется, динамически выводит тип на оба объекта и позволяет мне создавать экземпляры обоих моих объектов и вызывать их свойства и методы класса, которому они распределены, как в

using(var uow = new UnitOfWorkType1(), uow2 = new UnitOfWorkType2()){}.

Если кто-нибудь знает, почему это неправильно, сообщите мне

+1

Несколько на одной строке работают, если все вещи одного типа. Смешанные типы должны разбиваться на отдельные с помощью() s. Но это не работает с var, вы должны указать тип (спецификация C# 5, p237) –

Смежные вопросы