Aninhado usando instruções em c #

Eu estou trabalhando em um projeto. Eu tenho que comparar o conteúdo de dois arquivos e ver se eles correspondem um ao outro com precisão.

Antes de muita verificação de erros e validação, meu primeiro rascunho é:

DirectoryInfo di = new DirectoryInfo(Environment.CurrentDirectory + "\\TestArea\\"); FileInfo[] files = di.GetFiles(filename + ".*"); FileInfo outputFile = files.Where(f => f.Extension == ".out").Single(); FileInfo expectedFile = files.Where(f => f.Extension == ".exp").Single (); 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); } } 

Parece um pouco estranho ter nested using instruções.

Existe uma maneira melhor de fazer isso?

    A maneira preferida de fazer isso é apenas colocar uma chave de abertura { após a última instrução de using , assim:

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

    Se os objects forem do mesmo tipo, você pode fazer o seguinte

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

    Quando os IDisposable s são do mesmo tipo, você pode fazer o seguinte:

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

    A página do MSDN sobre o using tem documentação sobre esse recurso de idioma.

    Você pode fazer o seguinte, quer os IDisposable s sejam do mesmo tipo:

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

    Se você não se importar em declarar as variables ​​para o seu bloco de uso antes do bloco de uso, você pode declará-las todas na mesma instrução using.

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

    Dessa forma, x e y são apenas variables ​​de espaço reservado do tipo IDisponível para o bloco de uso a ser usado e você o usa dentro de seu código. Apenas pensei em mencionar.

    Se você quiser comparar os arquivos de forma eficiente, não use StreamReaders, e os usos não são necessários – você pode usar leituras de stream de baixo nível para extrair buffers de dados para comparar.

    Você também pode comparar coisas como o tamanho do arquivo primeiro para detectar rapidamente arquivos diferentes para evitar a necessidade de ler todos os dados também.

    A instrução using trabalha fora da interface IDisposable, portanto, outra opção poderia ser criar algum tipo de class composta que implemente IDisposable e tenha referências a todos os objects IDisposable que você normalmente colocaria em sua instrução using. O lado negativo disso é que você tem que declarar suas variables ​​primeiro e fora do escopo para que elas sejam úteis dentro do bloco de uso, exigindo mais linhas de código do que algumas das outras sugestões exigiriam.

     Connection c = new ...; Transaction t = new ...; using (new DisposableCollection(c, t)) { ... } 

    O construtor de DisposableCollection é um array params, neste caso, para que você possa alimentar quantos você quiser.

    Você também pode dizer:

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

    Mas algumas pessoas podem achar isso difícil de ler. BTW, como uma otimização para o seu problema, por que você não verifica se os tamanhos dos arquivos são do mesmo tamanho antes de ir linha por linha?

    Você pode omitir os colchetes em todos, exceto no mais interno, usando:

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

    Eu acho que isso é mais limpo do que colocar vários do mesmo tipo no mesmo uso, como outros sugeriram, mas tenho certeza que muitas pessoas vão pensar que isso é confuso

    Não há nada de estranho nisso. using é uma maneira abreviada de garantir o descarte do object quando o bloco de códigos é concluído. Se você tem um object descartável em seu bloco externo que o bloco interno precisa usar, isso é perfeitamente aceitável.

    Edit: muito lento na digitação para mostrar exemplo de código consolidado. +1 para todos os outros.

    E para aumentar a clareza, nesse caso, como cada instrução sucessiva é uma única instrução (e não um bloco), você pode omitir todos os colchetes:

     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; 

    Você pode agrupar vários objects descartáveis ​​em uma declaração de uso com vírgulas:

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

    Estes aparecem de tempos em tempos quando eu codifico também. Você poderia considerar mover a segunda instrução usando para outra function?

    Você também está perguntando se existe uma maneira melhor de comparar com arquivos? Eu prefiro calcular um CRC ou MD5 para ambos os arquivos e compará-los.

    Por exemplo, você poderia usar o seguinte método de extensão:

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

    Depois de fazer isso, é muito fácil comparar arquivos:

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

    Você poderia usar o construído na class System.Security.Cryptography.MD5, mas o hash calculado é um byte [] então você ainda teria que comparar esses dois arrays.

    Além disso, se você já conhece os caminhos, não faz sentido verificar o diretório.

    Em vez disso, eu recomendaria algo como isto:

     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 adicionará uma pasta ou um nome de arquivo a um caminho e garantirá que exista exatamente uma barra invertida entre o caminho e o nome.

    File.OpenText irá abrir um arquivo e criar um StreamReader uma só vez.

    Prefixando uma string com @, você pode evitar ter que escaping de todas as barras invertidas (por exemplo, @"a\b\c" )

    Eu acho que posso ter encontrado uma maneira sintaticamente mais limpa de declarar essa declaração usando, e parece funcionar para mim? usar var como seu tipo na instrução using em vez de IDisposable parece inferir dinamicamente o tipo em ambos os objects e permite-me instanciar meus dois objects e chamar suas propriedades e methods da class na qual eles estão alocados, como em

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

    Se alguém souber por que isso não está certo, por favor me avise