Imbriqué en utilisant les déclarations en C#
je travaille sur un projet. Je dois comparer le contenu de deux fichiers et voir s'ils concordent exactement.
devant beaucoup d'erreurs de vérification et de validation, mon premier projet est:
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);
}
}
il semble un peu étrange d'avoir imbriqué les déclarations using
.
Est-il une meilleure façon de le faire?
15 réponses
la façon préférée de faire ceci est de mettre seulement une attache d'ouverture {
après le dernier using
déclaration, comme ceci:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
///...
}
si les objets sont du même type vous pouvez faire ce qui suit
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()))
{
// ...
}
quand les IDisposable
s sont du même type, vous pouvez faire ce qui suit:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()) {
// ...
}
la page MSDN sur using
contient de la documentation sur cette caractéristique linguistique.
vous pouvez faire ce qui suit si oui ou non les IDisposable
s sont du même type:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamWriter anotherFile = new StreamReader(anotherFile.OpenRead()))
{
// ...
}
si cela ne vous dérange pas de déclarer les variables pour votre bloc d'utilisation avant le bloc d'utilisation, vous pouvez les déclarer toutes dans la même instruction d'utilisation.
Test t;
Blah u;
using (IDisposable x = (t = new Test()), y = (u = new Blah())) {
// whatever...
}
de cette façon, x et y sont juste des variables placeholder de type IDisposable pour le bloc d'utilisation à utiliser et vous utilisez t et u dans votre code. Juste pensé que je le mentionne.
si vous voulez comparer les fichiers efficacement, n'utilisez pas StreamReaders du tout, et puis les utilisations ne sont pas nécessaires - vous pouvez utiliser des lectures de flux de bas niveau pour tirer dans les tampons de données pour comparer.
vous pouvez également comparer des choses comme la taille du fichier d'abord pour détecter rapidement les différents fichiers pour vous éviter d'avoir à lire toutes les données, aussi.
l'instruction using fonctionne hors de L'interface IDisposable donc une autre option pourrait être de créer un type de classe composite qui implémente IDisposable et a des références à tous les objets Idisposables que vous mettriez normalement dans votre instruction using. L'inconvénient de cette est que vous devez déclarer vos variables d'abord et à l'extérieur de la portée de leur être utile dans l'utilisation de bloc nécessitant plus de lignes de code que quelques autres suggestions.
Connection c = new ...;
Transaction t = new ...;
using (new DisposableCollection(c, t))
{
...
}
le constructeur pour la collecte jetable est un tableau params dans ce cas donc vous pouvez vous alimenter en autant que vous le souhaitez.
vous pouvez aussi dire:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
...
}
Mais certaines personnes pourraient trouver difficile à lire. BTW, comme une optimisation à votre problème, pourquoi ne pas vérifier que les tailles de fichier sont la même taille d'abord, avant de passer ligne par ligne?
vous pourriez omettre les crochets sur tous sauf le plus intérieur en utilisant:
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;
}
}
}
je pense que c'est plus propre que de mettre plusieurs du même type dans la même utilisation, comme d'autres l'ont suggéré, mais je suis sûr que beaucoup de gens penseront que c'est déroutant
il n'y a rien d'étrange. using
est une façon abrégée d'assurer l'élimination de l'objet une fois que le bloc de codes est terminé. Si vous avez un objet jetable dans votre bloc extérieur que le bloc intérieur doit utiliser, c'est parfaitement acceptable.
éditer: trop lent sur la Dactylographie pour montrer exemple de code consolidé. +1 à tous les autres.
et pour juste ajouter à la clarté, dans ce cas, puisque chaque déclaration successive est une déclaration unique, (et non un bloc), vous pouvez omettre tous les crochets :
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;
vous pouvez grouper plusieurs objets jetables en un seul en utilisant-déclaration avec des virgules:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()))
{
}
C'est le moment où je coderai aussi. Vous pourriez envisager de déplacer le second en utilisant la déclaration dans une autre fonction?
vous demandez aussi s'il y a une meilleure façon de comparer les fichiers? Je préfère calculer un CRC OU MD5 pour les deux fichiers et les comparer.
par exemple, vous pouvez utiliser la méthode d'extension suivante:
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;
}
une fois que vous avez fait qu'il est assez facile de comparer des fichiers:
public bool filesAreEqual(string outFile, string expFile)
{
var outFileBytes = File.ReadAllBytes(outFile);
var expFileBytes = File.ReadAllBytes(expFile);
return (outFileBytes.CalculateCRC16() == expFileBytes.CalculateCRC16());
}
vous pouvez utiliser le système intégré.Sécurité.Cryptographie.Classe MD5, mais le hash calculé est un byte [] donc vous avez toujours pour comparer les deux tableaux.
aussi, si vous connaissez déjà les chemins, il n'y a pas de raison de scanner le répertoire.
à la place, je recommande quelque chose comme ceci:
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
va ajouter un dossier ou un nom de fichier à un chemin et s'assurer qu'il y a exactement un antislash entre le chemin et le nom.
File.OpenText
va ouvrir un fichier et créer un StreamReader
en une seule fois.
en préfixant une chaîne de caractères avec@, vous pouvez éviter d'avoir à échapper à chaque antislash (par exemple, @"a\b\c"
)
je pense que j'ai peut-être trouvé une façon syntaxiquement plus propre de déclarer ceci en utilisant la déclaration, et cela semble fonctionner pour moi? utiliser var comme votre type dans l'instruction using au lieu de IDisposable semble inférer dynamiquement le type sur les deux objets et me permet d'instancier les deux objets et d'appeler leurs propriétés et méthodes de la classe dans laquelle ils sont affectés, comme dans
using(var uow = new UnitOfWorkType1(), uow2 = new UnitOfWorkType2()){}.
Si quelqu'un sait pourquoi ce n'est pas juste, s'il vous plaît laissez-moi savoir