C #에서 문을 사용하여 중첩


315

프로젝트를 진행 중입니다. 두 파일의 내용을 비교하여 서로 정확히 일치하는지 확인해야합니다.

많은 오류 검사 및 유효성 검사 전에 첫 번째 초안은 다음과 같습니다.

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

중첩 된 using문장 을 갖는 것은 약간 이상하게 보입니다 .

더 좋은 방법이 있습니까?


구문을 사용하여 구문을 명확하게 선언하는 방법을 찾았을 것 같습니다. IDisposable 대신 using 문에서 var를 유형으로 사용하면 (var uow = UnitOfWorkType1 (), uow2 = UnitOfWorkType2와 같이 내 객체를 인스턴스화하고 할당 된 클래스의 속성과 메서드를 호출 할 수있는 것으로 보입니다. ()) {}
Caleb

답변:


556

이를 위해 선호되는 방법은 다음 과 같이 {마지막 using문장 뒤에 여는 중괄호 만 넣는 것입니다.

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

10
청소기? 또한 같은 유형을 사용하도록 강요하지는 않습니다. 가독성과 일관성을 위해 유형이 일치하더라도 항상이 방법을 사용합니다.
meandmycode 2012 년

7
@Hardryv : Visual Studio의 자동 형식이이를 제거합니다. 아이디어는 변수 선언 목록처럼 보이도록하는 것입니다.
SLaks

41
더 읽기 쉬운 지 확실하지 않습니다. 어떤 것이 있으면 중첩 코드의 모양이 깨집니다. 그리고 첫 번째 using 문이 비어 있고 사용되지 않는 것처럼 보입니다. / :하지만, 나는 지금 ... 작동하는 무슨 생각
조나단 Watney

10
@Bryan Watts, "반도 자"는 실제 선호도를 표현할 수 있습니다. 다른 그룹의 개발자가 반대 의견을 제시했을 가능성이 높습니다. 알 수있는 유일한 방법은 병렬 우주에서 실험을 다시 실행하는 것입니다.
Dan Rosenstark

6
@fmuecke : 그것은 사실이 아닙니다. 작동합니다. 두 번 IDisposable전화 Dispose()하는 것은 아무 것도하지 말아야한다는 주 규칙 . 이 규칙은 잘못 작성된 일회용품의 경우에만 해당됩니다.
SLaks

138

객체의 유형같은 경우 다음을 수행 할 수 있습니다.

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

1
만약 그들이 모두 캐스트가 가능할 것이라면 그것들은 모두 같은 타입입니까?
jpierson

8
작동하는 @jpierson, 그렇습니다.하지만 IDisposableusing 블록 내부 에서 객체를 호출 할 때 클래스 멤버를 호출 할 수 없습니다 (캐스트없이 포인트 imo를 물리 치기).
Connell

IDisposable은 유형이므로 다른 답변에서 볼 수 있듯이 유형을 혼합 유형 목록을 갖는 유형으로 사용하십시오.
Chris Rollins

33

IDisposable유형이 동일한 경우 다음을 수행 할 수 있습니다.

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

MSDN 페이지 using의이 언어 기능에 대한 설명서가 있습니다.

IDisposable유형이 같은지 여부에 관계없이 다음을 수행 할 수 있습니다 .

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

18

using 블록 전에 using 블록에 대한 변수를 선언하는 것이 마음에 들지 않으면 모두 동일한 using 문으로 선언 할 수 있습니다.

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

그렇게하면 x와 y는 using 블록에 사용할 IDisposable 유형의 자리 표시 자 변수 일 뿐이며 코드 내에서 t와 u를 사용합니다. 내가 언급 할 줄 알았는데


3
코드를보고있는 새로운 개발자에게는 혼란 스러울 것 같습니다.

5
이것은 나쁜 습관이 될 수 있습니다. 관리되지 않는 리소스가 해제 된 후에도 변수가 계속 존재한다는 부작용이 있습니다. Microsoft의 C # 참조에 따르면 "리소스 개체를 인스턴스화 한 다음 변수를 using 문에 전달할 수 있지만이 방법은 최선의 방법이 아닙니다.이 경우 컨트롤은 사용 블록을 떠나도 제어 블록이 사용 블록을 떠난 후에도 범위에 남아 있습니다. 더 이상 관리되지 않는 리소스에 액세스 할 수 없습니다. "
Robert Altman

@RobertAltman 맞습니다. 실제 코드에서는 다른 접근법 (아마 Gavin H의 접근법)을 사용합니다. 이것은 덜 선호되는 대안입니다.
Botz3000

with with typecasts로 선언을 이동할 수 있습니다. 더 나을까요?
Timothy Blaisdell

9

파일을 효율적으로 비교하려면 StreamReader를 전혀 사용하지 말고 사용하지 않아도됩니다. 낮은 수준의 스트림 읽기를 사용하여 비교할 데이터 버퍼를 가져올 수 있습니다.

또한 파일 크기와 같은 것을 먼저 비교하여 다른 파일을 빠르게 감지하여 모든 데이터를 읽을 필요가 없도록 할 수 있습니다.


예, 파일 크기를 확인하는 것이 좋습니다. 시간을 절약하거나 모든 바이트를 읽습니다. (+1)
TimothyP

9

using 문은 IDisposable 인터페이스에서 작동하므로 다른 옵션은 IDisposable을 구현하고 일반적으로 using 문에 넣을 모든 IDisposable 객체에 대한 참조를 갖는 복합 클래스 유형을 만드는 것입니다. 이것의 단점은 변수를 먼저 선언하고 범위 밖에서 변수를 선언해야 다른 제안 중 필요한 것보다 많은 코드 줄이 필요한 using 블록 내에서 변수가 유용하다는 것입니다.

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

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

이 경우 DisposableCollection의 생성자는 params 배열이므로 원하는만큼 입력 할 수 있습니다.


7

당신은 또한 말할 수 있습니다 :

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

그러나 어떤 사람들은 그것을 읽기가 어렵다는 것을 알게 될 것입니다. BTW는 문제를 최적화하기 위해 한 줄씩 이동하기 전에 파일 크기가 같은 크기인지 먼저 확인하지 않는 이유는 무엇입니까?


6

가장 안쪽을 제외하고 대괄호를 생략 할 수 있습니다.

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

다른 사람들이 제안한 것처럼 동일한 유형의 여러 제품을 동일한 용도로 사용하는 것보다 깨끗하다고 ​​생각하지만 많은 사람들이 이것이 혼란 스럽다고 생각할 것입니다


6

여러 개의 일회용 객체를 하나의 using 문에 쉼표로 그룹화 할 수 있습니다.

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

}

5

그것에 대해 이상한 것은 없습니다. using코드 블록이 완료되면 객체를 폐기하는 간단한 방법입니다. 내부 블록에 사용해야하는 일회용 블록이 외부 블록에있는 경우 이는 완벽하게 허용됩니다.

편집 : 통합 코드 예제를 표시하기 위해 입력 속도가 너무 느립니다. 다른 사람에게 +1


5

그리고 명확성을 더하기 위해이 경우 각 연속 명령문은 블록이 아닌 단일 명령문이므로 모든 괄호를 생략 할 수 있습니다.

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;  

흥미로운 해결책; 가장 낮은 수준에서 1 세트의 브래킷을 사용 하여이 작업을 수행하더라도 왼쪽 정렬 (깨끗한 IMO)을 쌓는 것과 동일한 목표를 달성하면서 다른 종속 관계를 보여주기 위해 언급 된 다른 사람의 미용 중첩 욕구를 해결합니다.
user1172173

5

C # 8.0 부터는 using 선언을 사용할 수 있습니다 .

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

변수 범위의 끝, 즉 메서드의 끝에서 사용 변수를 처리합니다.


3

이것들은 내가 코딩 할 때 때때로 나타납니다. 두 번째 using 문을 다른 함수로 옮기는 것을 고려할 수 있습니까?


3

파일과 비교하는 더 좋은 방법이 있는지 묻고 있습니까? 두 파일 모두에 대해 CRC 또는 MD5를 계산하고 비교하는 것을 선호합니다.

예를 들어 다음 확장 방법을 사용할 수 있습니다.

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

일단 파일을 비교하는 것은 매우 쉽습니다.

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

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

내장 된 System.Security.Cryptography.MD5 클래스를 사용할 수 있지만 계산 된 해시는 바이트 []이므로이 두 배열을 비교해야합니다.


2
바이트 배열을 취하는 대신, 메소드는 Stream객체를 가져 와서 ReadByte-1을 반환 할 때까지 메소드를 호출 해야합니다. 큰 파일의 경우 많은 양의 메모리가 절약됩니다.
SLaks

그런 다음 모든 바이트에서 crc를 어떻게 계산합니까?
TimothyP

아, 신경 쓰지 나는 말 : P 우리는 1000 바이트 그렇게하지 주목 문제가 아직이 있지만 어쨌든 변경됩니다 <데이터 사용 : P Thnx, 내 코드에서 그 변화거야
TimothyP

ReadByte스트림 을 호출 할 때마다 1 바이트 씩 진행됩니다. 따라서 -1 (EOF)을 반환 할 때까지 계속 호출하면 파일의 모든 바이트가 제공됩니다. msdn.microsoft.com/ko-kr/library/system.io.stream.readbyte.aspx
SLaks

7
여러 파일을 여러 번 비교하려면 CRC를 사용하는 것이 좋지만 단일 비교의 경우 CRC를 계산하기 위해 두 파일을 모두 읽어야합니다. 작은 청크로 데이터를 비교하는 경우 다음과 같이 비교를 종료 할 수 있습니다. 다른 바이트를 찾으면
Jason Williams

3

또한 이미 경로를 알고 있다면 디렉토리를 스캔하는 것이 중요하지 않습니다.

대신 다음과 같은 것이 좋습니다.

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 경로에 폴더 또는 파일 이름을 추가하고 경로와 이름 사이에 정확히 하나의 백 슬래시가 있는지 확인하십시오.

File.OpenText파일을 열고 한 번에 파일을 만듭니다 StreamReader.

@ 문자열을 접두어로하여 모든 백 슬래시를 탈출하는 것을 방지 할 수 있습니다 (예 @"a\b\c")


3

구문을 사용하여 구문을 명확하게 선언하는 방법을 찾았을 것 같습니다. 대신으로 IDisposable의 using 문에 형식이 보인다 VAR를 사용하여 동적으로 두 개체의 유형 추론과 내 객체 모두를 인스턴스화하고 같이 자신의 특성과 그들이 할당 된 클래스의 메소드를 호출 할 수 있습니다

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

사람이 이유를 알고 있다면 옳지 않다, 알려주세요


1
모든 것이 같은 유형이면 한 줄에 여러 개가 작동합니다. 혼합 유형은 별도의 using ()으로 분할해야합니다. 하지만 VAR 작동하지 않습니다, 당신은 유형을 지정해야합니다 (C # 5 사양, p237)
크리스 F 캐롤

0

일반적인 사용법이며 완벽하게 작동합니다. 이것을 구현하는 다른 방법이 있지만. 거의 모든 답변이이 질문의 답변에 이미 존재합니다. 그러나 여기에 나는 그것들을 모두 함께 나열하고 있습니다.

이미 사용 된

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

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

옵션 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;
       }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.