C # 'daki ifadeleri kullanarak iç içe


315

Bir proje üzerinde çalışıyorum. İki dosyanın içeriğini karşılaştırmak ve birbirleriyle tam olarak eşleşip eşleşmediklerini görmek zorundayım.

Birçok hata kontrolü ve doğrulamadan önce ilk taslağım:

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

Yuvalanmış usingifadelere sahip olmak biraz garip görünüyor .

Bunu yapmanın daha iyi bir yolu var mı?


Sanırım bu deyimi kullanarak bildirmek için sözdizimsel olarak daha temiz bir yol bulmuş olabilir ve bu benim için çalışıyor gibi görünüyor? IDisposable yerine using deyiminde var olarak var kullanmak, her iki nesneyi de somutlaştırmama ve kullanıldıkları gibi ayırdıkları sınıfın özelliklerini ve yöntemlerini çağırmama izin veriyor gibi görünüyor (var uow = UnitOfWorkType1 (), uow2 = UnitOfWorkType2 (}) {}
Caleb

Yanıtlar:


556

Bunu yapmanın tercih edilen yolu, yalnızca {son usingifadeden sonra bir açılış ayracı koymaktır , örneğin:

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

10
Temizleyici? ve aynı türleri kullanmaya da zorlamıyor .. Tipler okunabilirlik ve tutarlılıkla eşleşse bile her zaman böyle yaparım.
meandmycode

7
@ Hardryv: Visual Studio'nun otomatik biçimi onu kaldırır. Fikir, değişken bildirimlerin bir listesi gibi görünmektir.
SLaks

41
Daha okunaklı olup olmadığından emin değilim. Bir şey varsa iç içe kod görünümünü bozar. Ve ilk kullanım ifadesi boş ve kullanılmamış gibi görünüyor. Ama, sanırım şimdiye kadar ne işe yarıyor ...: /
Jonathon Watney

10
@Bryan Watts, "kontrarianslar" gerçek tercihleri ​​ifade ediyor olabilir. Yuvalama önerilmiş olsaydı, farklı bir grup geliştiricinin muhalif olması muhtemeldir. Bilmenin tek yolu deneyi paralel bir evrende tekrar çalıştırmaktır.
Dan Rosenstark

6
@fmuecke: Bu çok doğru değil; Çalışacak. İki kez IDisposableçağırmanın Dispose()hiçbir şey yapmaması gerektiğini belirten kurallar . Bu kural sadece kötü yazılmış tek kullanımlık durumlarda geçerlidir.
SLaks

138

Nesneler aynı türdeyse aşağıdakileri yapabilirsiniz

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

1
Peki hepsi hepsi düşünülemezse aynı türdüler belki de bir oyuncu işe yarayacaktır?
jpierson

8
@jpierson işe yarıyor, evet, ama sonra IDisposablenesneyi kullanma bloğunun içinden çağırdığınızda, sınıf üyelerinden hiçbirini çağıramayız (imo noktasını yenen bir kadro olmadan).
Connell

IDisposable bir türdür, bu yüzden diğer birkaç cevapta görüldüğü gibi karışık türlerin bir listesine sahip olmak için türü olarak kullanın.
Chris Rollins

33

Ne zaman IDisposableın aynı tipte olan, aşağıdakileri yapabilirsiniz:

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

Üzerindeki MSDN sayfasında usingbu dil özelliği ile ilgili belgeler var.

S'ler IDisposableaynı türde olsun ya da olmasın aşağıdakileri yapabilirsiniz :

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

18

eğer kullanma bloğundan önce kullanım bloğunuz için değişkenleri bildirmenin bir sakıncası yoksa, hepsini aynı kullanım ifadesinde bildirebilirsiniz.

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

Bu şekilde, x ve y, yalnızca kullanım bloğunun kullanması için uygun olan ID tipinde yer tutucu değişkenlerdir ve kodunuzun içinde t ve u kullanırsınız. Sadece bahsettiğimi düşündüm.


3
Bu kodunuzu bakarak yeni bir geliştirici için kafa karıştırıcı olacağını hissediyorum.
Zack

5
Bu kötü bir uygulama olabilir; yönetilmeyen kaynaklar serbest bırakıldıktan sonra bile değişkenlerin hala mevcut olacağı bir yan etkisi vardır. Microsoft'un C # başvurusuna göre, "Kaynak nesnesini somutlaştırabilir ve sonra değişkeni using deyimine iletebilirsiniz, ancak bu en iyi uygulama değildir. Bu durumda, denetim, denetim bloğunun muhtemelen artık yönetilmeyen kaynaklarına erişemiyor. "
Robert Altman

@RobertAltman Haklısınız ve gerçek kodda başka bir yaklaşım kullanacağım (muhtemelen Gavin H'den yaklaşım). Bu daha az tercih edilen bir alternatif.
Botz3000

Deklarasyonları kullanarak daktiloyla taşıyabilirsiniz. Bu daha iyi olur mu?
Timothy Blaisdell

9

Dosyaları verimli bir şekilde karşılaştırmak istiyorsanız, StreamReaders'ı hiç kullanmayın ve daha sonra kullanımlar gerekli değildir - karşılaştırmak için veri tamponlarını çekmek için düşük düzeyli akış okumalarını kullanabilirsiniz.

Ayrıca, tüm dosyaları okumak zorunda kalmamak için farklı dosyaları hızlı bir şekilde algılamak için önce dosya boyutu gibi şeyleri de karşılaştırabilirsiniz.


Evet, dosya boyutunu kontrol etmek iyi bir fikirdir, size zaman kazandırır veya tüm baytları okur. (+1)
TimothyP

9

Using deyimi, IDisposable arabiriminin dışında çalışır, bu nedenle başka bir seçenek, IDisposable'ı uygulayan ve normalde using deyiminize koyacağınız tüm IDisposable nesnelerine başvurular içeren bir tür bileşik sınıf oluşturmak olabilir. Bunun aşağı tarafı, değişkenlerinizi ilk önce ve kapsam dışında, diğer önerilerin bazılarının gerektirdiğinden daha fazla kod satırı gerektiren kullanım bloğu içinde faydalı olmaları için bildirmeniz gerektiğidir.

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

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

DisposableCollection yapıcısı bu durumda bir params dizisidir, böylece istediğiniz kadar besleyebilirsiniz.


7

Ayrıca şunları da söyleyebilirsiniz:

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

Ancak bazı insanlar bunu okumayı zor bulabilir. BTW, sorununuza bir optimizasyon olarak, satır satır gitmeden önce neden dosya boyutlarının aynı boyutta olduğunu kontrol etmiyorsunuz?


6

Köşeli parantezleri en içteki dışındaki tüm köşeleri hariç tutabilirsiniz:

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

Bence bu, diğerlerinin önerdiği gibi, aynı türden birkaçını aynı şekilde kullanmaktan daha temiz, ancak eminim birçok insan bunun kafa karıştırıcı olduğunu düşünecek


6

Birden çok tek kullanımlık nesneyi virgül içeren bir deyim kullanarak gruplayabilirsiniz:

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

}

5

Bunda tuhaf bir şey yok. usingkod bloğu bittikten sonra nesnenin atılmasını sağlamanın kısa yoludur. Dış bloğunuzda iç bloğun kullanması gereken tek kullanımlık bir nesneniz varsa, bu tamamen kabul edilebilir.

Düzenle: Birleştirilmiş kod örneğini göstermek için yazarken çok yavaş. Herkese + 1'leyin.


5

Ve sadece netlik eklemek için, bu durumda, birbirini takip eden her ifade tek bir ifade olduğundan (ve bir blok değil), tüm parantezleri atlayabilirsiniz:

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;  

İlginç bir çözüm; bunu yapmak / hatta 1 kümeyi en düşük seviyede kullanmak, belki de sola yaslanmış (daha temiz IMO) istiflemekle aynı hedefe ulaşırken, diğerlerinin herhangi bir boyun eğme gösterdiği belirtilen kozmetik yuvalama arzusunu ele alır.
user1172173

5

C # 8.0'dan beri bir kullanım bildirimi kullanabilirsiniz .

using var outFile = new StreamReader(outputFile.OpenRead());
using var expFile = new StreamReader(expectedFile.OpenRead());
while (!(outFile.EndOfStream || expFile.EndOfStream))
{
    if (outFile.ReadLine() != expFile.ReadLine())
    {
         return false;
    }
}
return (outFile.EndOfStream && expFile.EndOfStream);

Bu, kullanılan değişkenleri değişkenlerin kapsamının sonunda, yani yöntemin sonunda bertaraf edecektir.


3

Bunlar da zaman zaman ben de kod. İkinci kullanım ifadesini başka bir işleve taşımayı düşünebilirsiniz.


3

Dosyalarla karşılaştırmanın daha iyi bir yolu olup olmadığını mı soruyorsunuz? Her iki dosya için bir CRC veya MD5 hesaplamayı tercih ediyorum ve bunları karşılaştırıyorum.

Örneğin, aşağıdaki uzantı yöntemini kullanabilirsiniz:

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

Bunu yaptıktan sonra dosyaları karşılaştırmak oldukça kolaydır:

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

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

Yerleşik System.Security.Cryptography.MD5 sınıfını kullanabilirsiniz, ancak hesaplanan karma bir bayttır [], bu nedenle bu iki diziyi karşılaştırmanız gerekir.


2
Bir bayt dizisi almak yerine, yöntem bir Streamnesne alıp ReadByte-1 döndürene kadar yöntemi çağırmalıdır . Bu, büyük dosyalar için büyük miktarda bellek tasarrufu sağlayacaktır.
SLaks

Daha sonra tüm baytlar üzerindeki crc'yi nasıl hesaplarsınız?
TimothyP

Oh ne dediğime aldırma: p Thnx, kodumda değiştireceğim: p Biz sadece <1000 byte veri için kullanıyoruz, bu yüzden henüz problemleri fark etmedim ama yine de değişecek
TimothyP

ReadByteAkışın konumunu her aradığınızda bir bayt ilerler. Bu nedenle, -1 (EOF) dönene kadar aramaya devam ederseniz, dosyadaki her baytı verir. msdn.microsoft.com/en-us/library/system.io.stream.readbyte.aspx
SLaks

7
Birden çok dosyayı birden çok kez karşılaştırmak istiyorsanız bir CRC kullanmak harikadır, ancak tek bir karşılaştırma için CRC'leri hesaplamak için her iki dosyayı da tamamen okumalısınız - Küçük parçalar halinde verileri karşılaştırırsanız, karşılaştırmadan çıkabilirsiniz. farklı bir bayt bulduğunuzda.
Jason Williams

3

Ayrıca, yolları zaten biliyorsanız, dizini taramanın bir anlamı yoktur.

Bunun yerine, böyle bir şey tavsiye ederim:

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 yola bir klasör veya dosya adı ekler ve yol ile ad arasında tam olarak bir ters eğik çizgi olduğundan emin olun.

File.OpenTextbir dosyayı açacak ve StreamReadertek seferde bir dosya oluşturacak .

Bir dizeye @ ekleyerek her ters eğik çizgiden (ör. @"a\b\c") Kaçmak zorunda kalmazsınız.


3

Sanırım bu deyimi kullanarak bildirmek için sözdizimsel olarak daha temiz bir yol bulmuş olabilir ve bu benim için çalışıyor gibi görünüyor? yerine IDisposal kullanarak açıklamada senin tipin göründüğü gibi var kullanarak dinamik olarak her iki nesneler üzerinde tip anlaması ve bana nesnelerin hem örneğini ve bunların özellikleri ve onlar ile ayrılır sınıfının, yöntemlerini çağırmak için izin veren

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

herkes neden bu biliyorsa doğru değil, lütfen bana bildirin


1
Her şey aynı türdeyse, bir satırda birkaç tane çalışır. Karışık türler () s kullanılarak ayrı olarak ayrılmalıdır. Ancak var ile çalışmaz, bir tür belirtmeniz gerekir (C # 5 belirtimi, p237)
Chris F Carroll

0

Bu normal kullanım şekli ve mükemmel çalışıyor. Bunu uygulamanın başka yolları olsa da. Bu sorunun cevabında hemen hemen her cevap zaten mevcut. Ama burada hepsini bir araya getiriyorum.

Zaten kullanılmış

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

seçenek 1

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

seçenek 2

using (StreamReader outFile = new StreamReader(outputFile.OpenRead()),
                    expFile = new StreamReader(expectedFile.OpenRead()))
   {
      while (!(outFile.EndOfStream || expFile.EndOfStream))
       {
         if (outFile.ReadLine() != expFile.ReadLine())
         return false;
       }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.