Verschachtelt mit statementen in C #

Ich arbeite an einem Projekt. Ich muss den Inhalt von zwei Dateien vergleichen und sehen, ob sie genau übereinstimmen.

Vor einer großen Fehlerprüfung und -validierung lautet mein erster Entwurf:

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

Es scheint etwas seltsam zu sein, using statementen verschachtelt zu sein.

Gibt es einen besseren Weg, dies zu tun?

   

Der bevorzugte Weg ist, nur eine öffnende Klammer { nach der letzten using statement, wie folgt zu setzen:

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

Wenn die Objekte vom selben Typ sind , können Sie Folgendes tun

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

Wenn IDisposable s vom selben Typ sind, können Sie Folgendes tun:

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

Die MSDN-Seite zum using enthält Dokumentation zu dieser Sprachfunktion.

Sie können Folgendes tun, unabhängig davon, ob IDisposable s vom selben Typ sind oder nicht:

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

Wenn es Ihnen nichts ausmacht, die Variablen für Ihren using-Block vor dem using-Block zu deklarieren, können Sie sie alle in derselben using-statement deklarieren.

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

Auf diese Weise sind x und y nur Platzhaltervariablen vom Typ IDisposable für den verwendeten Block und Sie verwenden t und u in Ihrem Code. Ich dachte nur, ich würde es erwähnen.

Wenn Sie die Dateien effizient vergleichen möchten, verwenden Sie StreamReaders überhaupt nicht, und dann ist die Verwendung nicht erforderlich. Sie können Stream-Lesevorgänge auf niedriger Ebene verwenden, um Puffer für zu vergleichende Daten abzurufen.

Sie können auch Dinge wie die Dateigröße vergleichen, um schnell verschiedene Dateien zu finden, um sich selbst zu retten und alle Daten zu lesen.

Die using-statement arbeitet von der IDisposable-Schnittstelle ab, sodass eine andere Option darin bestehen könnte, eine Art zusammengesetzte class zu erstellen, die IDisposable implementiert und Verweise auf alle IDisposable-Objekte enthält, die Sie normalerweise in Ihre using-statement einfügen würden. Der Nachteil davon ist, dass Sie Ihre Variablen zuerst und außerhalb des Bereichs deklarieren müssen, damit sie innerhalb des using-Blocks nützlich sind, der mehr Codezeilen erfordert, als einige der anderen Vorschläge erfordern würden.

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

Der Konstruktor für DisposableCollection ist in diesem Fall ein params-Array, sodass Sie so viele Feeds eingeben können, wie Sie möchten.

Du kannst auch sagen:

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

Aber manche Leute finden das vielleicht schwer zu lesen. BTW, als eine Optimierung für Ihr Problem, warum überprüfen Sie nicht, dass die Dateigrößen zuerst die gleiche Größe haben, bevor Sie Zeile für Zeile gehen?

Sie können die Klammern auf allen außer den innersten verwenden mit:

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

Ich denke, das ist sauberer, als wenn man mehrere der gleichen Typen in die gleiche Anwendung bringt, wie andere vorgeschlagen haben, aber ich bin sicher, dass viele Leute denken werden, dass dies verwirrend ist

Das ist nicht merkwürdig. using ist eine Kurzform, um die Entsorgung des Objekts sicherzustellen, sobald der Codeblock fertig ist. Wenn Sie in Ihrem äußeren Block ein Einwegobjekt haben, das der innere Block verwenden muss, ist dies vollkommen akzeptabel.

Bearbeiten: Zu langsam bei der Eingabe, um das Beispiel für konsolidierten Code anzuzeigen. +1 für alle anderen.

Und um nur zur Klarheit beizutragen, in diesem Fall, da jede nachfolgende statement eine einzelne statement ist (und kein Block), können Sie alle Klammern weglassen:

 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; 

Sie können mehrere Einwegobjekte in einer using-statement mit Kommas gruppieren:

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

Diese kommen von Zeit zu Zeit, wenn ich auch Code schreibe. Sie könnten in Erwägung ziehen, die zweite using-statement in eine andere function zu verschieben?

Fragen Sie auch, ob es einen besseren Weg gibt, mit Dateien zu vergleichen? Ich bevorzuge es, einen CRC oder MD5 für beide Dateien zu berechnen und diese zu vergleichen.

Zum Beispiel könnten Sie die folgende Erweiterungsmethode verwenden:

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

Sobald Sie das getan haben, ist es ziemlich einfach, Dateien zu vergleichen:

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

Sie könnten die integrierte class System.Security.Cryptography.MD5 verwenden, aber der berechnete Hash ist ein Byte [], sodass Sie diese beiden Arrays immer noch vergleichen müssen.

Wenn Sie die Pfade bereits kennen, ist das Scannen des Verzeichnisses sinnlos.

Stattdessen würde ich Folgendes empfehlen:

 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 fügt einem Pfad einen Ordner oder Dateinamen hinzu und stellt sicher, dass zwischen dem Pfad und dem Namen genau ein Path.Combine Schrägstrich vorhanden ist.

File.OpenText öffnet eine Datei und erstellt auf einmal einen StreamReader .

Indem Sie eine Zeichenfolge mit @ voranstellen, können Sie vermeiden, jeden umgekehrten Schrägstrich zu entziffern (zB @"a\b\c" )

Ich denke, dass ich eine syntaktisch sauberere Art gefunden habe, diese Aussage zu erklären, und es scheint für mich zu funktionieren? Die Verwendung von var als Typ in der using-statement anstelle von IDisposable scheint den Typ dynamisch auf beide Objekte zu beziehen und erlaubt mir, beide Objekte zu instanziieren und ihre Eigenschaften und Methoden der class aufzurufen, der sie zugeordnet sind, wie in

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

Wenn jemand weiß, warum das nicht stimmt, lass es mich wissen