연습: Visual Basic에서 IEnumerable(Of T) 구현

IEnumerable<T> 인터페이스는 한 번에 하나의 항목 값 시퀀스를 반환할 수 있는 클래스에 의해 구현됩니다. 데이터를 한 번에 하나의 항목으로 반환할 때의 이점은 전체 데이터 집합을 메모리에 로드하여 작업할 필요가 없다는 것입니다. 데이터에서 단일 항목을 로드하려면 충분한 메모리만 사용해야 합니다. IEnumerable(T) 인터페이스를 구현하는 클래스는 For Each 루프 또는 LINQ 쿼리와 함께 사용할 수 있습니다.

예를 들어 큰 텍스트 파일을 읽고 특정 검색 조건과 일치하는 파일에서 각 줄을 반환해야 하는 애플리케이션을 생각해 보세요. 애플리케이션은 LINQ 쿼리를 사용하여 지정된 조건과 일치하는 파일의 줄을 반환합니다. LINQ 쿼리를 사용하여 파일 내용을 쿼리하기 위해 애플리케이션은 파일의 내용을 배열 또는 컬렉션에 로드할 수 있습니다. 그러나 전체 파일을 배열 또는 컬렉션에 로드하면 필요한 것보다 훨씬 더 많은 메모리가 소비됩니다. LINQ 쿼리는 열거 가능한 클래스를 사용하여 파일 내용을 쿼리하고 검색 조건과 일치하는 값만 반환할 수 있습니다. 일치하는 값 몇 가지만 반환하는 쿼리는 메모리를 훨씬 적게 사용합니다.

IEnumerable<T> 인터페이스를 구현하여 원본 데이터를 열거 가능한 데이터로 노출하는 클래스를 만들 수 있습니다. IEnumerable(T) 인터페이스를 구현하는 클래스에는 원본 데이터를 반복하기 위해 IEnumerator<T> 인터페이스를 구현하는 다른 클래스가 필요합니다. 이러한 두 클래스를 사용하면 데이터의 항목을 특정 형식으로 순차적으로 반환할 수 있습니다.

IEnumerable(Of String) 인터페이스를 구현하는 클래스와 IEnumerator(Of String) 인터페이스를 구현하는 클래스를 만들어 한 번에 한 줄씩 텍스트 파일을 읽는 방법을 보여 줍니다.

참고 항목

일부 Visual Studio 사용자 인터페이스 요소의 경우 다음 지침에 설명된 것과 다른 이름 또는 위치가 시스템에 표시될 수 있습니다. 이러한 요소는 사용하는 Visual Studio 버전 및 설정에 따라 결정됩니다. 자세한 내용은 IDE 개인 설정을 참조하세요.

열거 가능한 클래스 만들기

열거 가능한 클래스 프로젝트 만들기

  1. Visual Studio의 파일 메뉴에서 새로 만들기를 가리킨 다음, 프로젝트를 클릭합니다.

  2. 새 프로젝트 대화 상자의 프로젝트 형식 창에서 Windows가 선택되었는지 확인합니다. 템플릿 창에서 클래스 라이브러리를 선택합니다. 이름 상자에 StreamReaderEnumerable를 입력한 다음 확인을 클릭합니다. 새 프로젝트가 표시됩니다.

  3. 솔루션 탐색기에서 Class1.vb 파일을 마우스 오른쪽 단추로 클릭하고 이름 바꾸기를 클릭합니다. 파일 이름을 StreamReaderEnumerable.vb로 바꾸고 ENTER 키를 누릅니다. 파일 이름을 바꾸면 클래스 이름도 StreamReaderEnumerable로 바뀝니다. 이 클래스는 IEnumerable(Of String) 인터페이스를 구현합니다.

  4. StreamReaderEnumerable 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음, 새 항목을 클릭합니다. 클래스 템플릿을 선택합니다. 이름 상자에 StreamReaderEnumerator.vb를 입력하고 확인을 클릭합니다.

이 프로젝트의 첫 번째 클래스는 열거 가능한 클래스이며 IEnumerable(Of String) 인터페이스를 구현합니다. 이 제네릭 인터페이스는 IEnumerable 인터페이스를 구현하고 이 클래스의 소비자가 String로 입력된 값에 액세스할 수 있도록 보장합니다.

IEnumerable을 구현하는 코드 추가

  1. StreamReaderEnumerable.vb 파일을 엽니다.

  2. Public Class StreamReaderEnumerable 다음 줄에서 다음을 입력하고 Enter 키를 누릅니다.

    Implements IEnumerable(Of String)
    

    Visual Basic은 IEnumerable(Of String) 인터페이스에 필요한 멤버로 클래스를 자동으로 채웁니다.

  3. 이 열거 가능한 클래스는 한 번에 한 줄씩 텍스트 파일에서 줄을 읽습니다. 다음 코드를 클래스에 추가하여 파일 경로를 입력 매개 변수로 사용하는 퍼블릭 생성자를 노출합니다.

    Private _filePath As String
    
    Public Sub New(ByVal filePath As String)
        _filePath = filePath
    End Sub
    
  4. IEnumerable(Of String) 인터페이스의 GetEnumerator 메서드 구현은 StreamReaderEnumerator 클래스의 새 인스턴스를 반환합니다. IEnumerable(Of String) 인터페이스의 멤버만 노출해야 하므로 IEnumerable 인터페이스의 GetEnumerator 메서드의 구현을 Private으로 만들 수 있습니다. GetEnumerator 메서드에 대해 Visual Basic에서 생성한 코드를 다음 코드로 바꿉니다.

    Public Function GetEnumerator() As IEnumerator(Of String) _
        Implements IEnumerable(Of String).GetEnumerator
    
        Return New StreamReaderEnumerator(_filePath)
    End Function
    
    Private Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator
    
        Return Me.GetEnumerator()
    End Function
    

IEnumerator를 구현하는 코드 추가

  1. StreamReaderEnumerator.vb 파일을 엽니다.

  2. Public Class StreamReaderEnumerator 다음 줄에서 다음을 입력하고 Enter 키를 누릅니다.

    Implements IEnumerator(Of String)
    

    Visual Basic은 IEnumerator(Of String) 인터페이스에 필요한 멤버로 클래스를 자동으로 채웁니다.

  3. 열거자 클래스는 텍스트 파일을 열고 파일 I/O를 수행하여 파일에서 줄을 읽습니다. 다음 코드를 클래스에 추가하여 파일 경로를 입력 매개 변수로 사용하는 퍼블릭 생성자를 노출하고 읽기 위해 텍스트 파일을 엽니다.

    Private _sr As IO.StreamReader
    
    Public Sub New(ByVal filePath As String)
        _sr = New IO.StreamReader(filePath)
    End Sub
    
  4. IEnumerator(Of String)IEnumerator 두 인터페이스에 대한 Current 속성은 텍스트 파일의 현재 항목을 String로 반환합니다. IEnumerator(Of String) 인터페이스의 멤버만 노출해야 하므로 IEnumerator 인터페이스의 Current 속성을 구현을 Private으로 만들 수 있습니다. Current 속성에 대해 Visual Basic에서 생성한 코드를 다음 코드로 바꿉니다.

    Private _current As String
    
    Public ReadOnly Property Current() As String _
        Implements IEnumerator(Of String).Current
    
        Get
            If _sr Is Nothing OrElse _current Is Nothing Then
                Throw New InvalidOperationException()
            End If
    
            Return _current
        End Get
    End Property
    
    Private ReadOnly Property Current1() As Object _
        Implements IEnumerator.Current
    
        Get
            Return Me.Current
        End Get
    End Property
    
  5. IEnumerator 인터페이스의 MoveNext 메서드는 텍스트 파일의 다음 항목으로 이동하여 Current 속성에서 반환되는 값을 업데이트합니다. 읽을 항목이 더 이상 없으면 MoveNext 메서드가 False로 반환되고, 그러지 않으면 MoveNext 메서드가 True로 반환됩니다. MoveNext 메서드에 다음 코드를 추가합니다.

    Public Function MoveNext() As Boolean _
        Implements System.Collections.IEnumerator.MoveNext
    
        _current = _sr.ReadLine()
        If _current Is Nothing Then Return False
        Return True
    End Function
    
  6. IEnumerator 인터페이스의 Reset 메서드는 반복기가 텍스트 파일의 시작을 가리키도록 지시하고 현재 항목 값을 지웁니다. Reset 메서드에 다음 코드를 추가합니다.

    Public Sub Reset() _
        Implements System.Collections.IEnumerator.Reset
    
        _sr.DiscardBufferedData()
        _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
        _current = Nothing
    End Sub
    
  7. IEnumerator 인터페이스의 Dispose 메서드는 반복기가 제거되기 전에 관리되지 않는 모든 리소스가 해제되도록 보장합니다. StreamReader 개체에서 사용하는 파일 핸들은 관리되지 않는 리소스이며 반복기 인스턴스가 제거되기 전에 닫아야 합니다. Visual Basic에서 Dispose 메서드에 대해 생성한 코드를 다음 코드로 바꿉니다.

    Private disposedValue As Boolean = False
    
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' Dispose of managed resources.
            End If
            _current = Nothing
            _sr.Close()
            _sr.Dispose()
        End If
    
        Me.disposedValue = True
    End Sub
    
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
    

샘플 반복기 사용

For Next 루프 또는 LINQ 쿼리와 같이 IEnumerable을 구현하는 개체가 필요한 컨트롤 구조체와 함께 코드에서 열거 가능한 클래스를 사용할 수 있습니다. 다음 예제에서는 LINQ 쿼리에 StreamReaderEnumerable이 있는 것을 보여 줄 수 있습니다.

Dim adminRequests =
    From line In New StreamReaderEnumerable("..\..\log.txt")
    Where line.Contains("admin.aspx 401")

Dim results = adminRequests.ToList()

참고 항목