estructura - Anidado usando declaraciones en C#
clases y metodos en c# (15)
¿Pienso que puedo haber encontrado una forma sintácticamente más limpia de declarar esto usando una declaración, y parece funcionar para mí? usar var como su tipo en la declaración de uso en lugar de IDisposable parece inferir dinámicamente el tipo en ambos objetos y me permite crear una instancia de mis objetos y llamar a sus propiedades y métodos de la clase con la que están asignados, como
using(var uow = new UnitOfWorkType1(), uow2 = new UnitOfWorkType2()){}.
Si alguien sabe por qué esto no está bien, hágamelo saber
Estoy trabajando en un proyecto. Tengo que comparar el contenido de dos archivos y ver si coinciden entre sí con precisión.
Antes de un montón de comprobación de errores y validación, mi primer borrador es:
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);
}
}
Parece un poco extraño tener anidados using
sentencias.
¿Hay una mejor manera de hacer esto?
¿También estás preguntando si hay una mejor manera de comparar los archivos? Prefiero calcular un CRC o MD5 para ambos archivos y compararlos.
Por ejemplo, podrías usar el siguiente método de extensión:
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;
}
Una vez que hayas hecho eso, es bastante fácil comparar archivos:
public bool filesAreEqual(string outFile, string expFile)
{
var outFileBytes = File.ReadAllBytes(outFile);
var expFileBytes = File.ReadAllBytes(expFile);
return (outFileBytes.CalculateCRC16() == expFileBytes.CalculateCRC16());
}
Podría usar la clase System.Security.Cryptography.MD5 incorporada, pero el hash calculado es un byte [], por lo que aún tendría que comparar esas dos matrices.
Además, si ya conoce las rutas, no tiene sentido escanear el directorio.
En su lugar, recomendaría algo como esto:
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
agregará una carpeta o nombre de archivo a una ruta y se asegurará de que exista exactamente una barra invertida entre la ruta y el nombre.
File.OpenText
abrirá un archivo y creará un StreamReader
de una sola vez.
Al prefijar una cadena con @, puede evitar tener que escapar de cada barra invertida (por ejemplo, @"a/b/c"
)
Cuando los IDisposable
son del mismo tipo, puede hacer lo siguiente:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()) {
// ...
}
La página de MSDN sobre el using
tiene documentación sobre esta característica de idioma.
Puede hacer lo siguiente si los IDisposable
son del mismo tipo o no:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamWriter anotherFile = new StreamReader(anotherFile.OpenRead()))
{
// ...
}
Estos vienen de vez en cuando cuando código también. ¿Podrías considerar mover la segunda instrucción usando otra función?
La declaración de uso funciona fuera de la interfaz IDisposable, por lo que otra opción podría ser crear algún tipo de clase compuesta que implemente IDisposable y tenga referencias a todos los objetos IDisposibles que normalmente pondría en su declaración de uso. El aspecto negativo de esto es que debe declarar sus variables primero y fuera del alcance para que sean útiles dentro del bloque de uso que requiere más líneas de código de las que requerirían algunas de las otras sugerencias.
Connection c = new ...;
Transaction t = new ...;
using (new DisposableCollection(c, t))
{
...
}
El constructor de DisposableCollection es una matriz params en este caso, por lo que puede introducir tantas como desee.
La forma preferida de hacer esto es poner solo una llave de apertura {
después de la última instrucción de using
, como esta:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
///...
}
No hay nada extraño en ello. using
es una forma abreviada de garantizar la eliminación del objeto una vez que finaliza el bloque de código. Si tiene un objeto desechable en su bloque exterior que el bloque interno necesita usar, esto es perfectamente aceptable.
Edición: demasiado lento en la escritura para mostrar el ejemplo de código consolidado. +1 a todos los demás.
Puede agrupar varios objetos desechables en una declaración de uso con comas:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()))
{
}
Puede omitir los paréntesis en todos, excepto en el más interno, utilizando:
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;
}
}
}
Creo que esto es más limpio que poner varios del mismo tipo en el mismo uso, como han sugerido otros, pero estoy seguro de que mucha gente pensará que esto es confuso
Si desea comparar los archivos de manera eficiente, no use StreamReaders en absoluto, y luego los usos no son necesarios, puede usar lecturas de flujo de bajo nivel para extraer buffers de datos para comparar.
También puede comparar cosas como el tamaño del archivo primero para detectar rápidamente diferentes archivos para evitar tener que leer todos los datos, también.
Si los objetos son del mismo tipo puedes hacer lo siguiente
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
expFile = new StreamReader(expectedFile.OpenRead()))
{
// ...
}
Si no le importa declarar las variables para su bloque de uso antes del bloque de uso, puede declararlas todas en la misma instrucción de uso.
Test t;
Blah u;
using (IDisposable x = (t = new Test()), y = (u = new Blah())) {
// whatever...
}
De esa manera, xey son simplemente variables de marcador de posición de tipo IDisposable para el uso del bloque y usas t y u dentro de tu código. Solo pensé que lo mencionaría.
También puede decir:
using (StreamReader outFile = new StreamReader(outputFile.OpenRead()))
using (StreamReader expFile = new StreamReader(expectedFile.OpenRead()))
{
...
}
Pero algunas personas pueden encontrar eso difícil de leer. Por cierto, como una optimización de su problema, ¿por qué no comprueba que los tamaños de archivo sean del mismo tamaño antes de ir línea por línea?
Y para agregar más claridad, en este caso, dado que cada declaración sucesiva es una declaración única (y no un bloque), puede omitir todos los corchetes:
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;