2014-10-09 4 views
2

제네릭 컬렉션 클래스에서 IEnumerable (일반) 인터페이스를 사용하여 IEnumerable (비 제네릭)을 구현해야하는 이유는 무엇입니까?
MSDN의 상태 (링크 - http://msdn.microsoft.com/en-us/library/9eekhta0(v=vs.110).aspx)에 코드 예제
ienumerable with ienumerable 구현 <T>

public class App 
{ 
// Excercise the Iterator and show that it's more 
// performant. 
public static void Main() 
{ 
    TestStreamReaderEnumerable(); 
    TestReadingFile(); 
} 

public static void TestStreamReaderEnumerable() 
{ 
    // Check the memory before the iterator is used. 
    long memoryBefore = GC.GetTotalMemory(true); 

    // Open a file with the StreamReaderEnumerable and check for a string. 
    var stringsFound = 
     from line in new StreamReaderEnumerable(@"c:\\temp\\tempFile.txt") 
     where line.Contains("string to search for") 
     select line; 


    Console.WriteLine("Found: " + stringsFound.Count()); 

    // Check the memory after the iterator and output it to the console. 
    long memoryAfter = GC.GetTotalMemory(false); 
    Console.WriteLine("Memory Used With Iterator = \t" 
     + string.Format(((memoryAfter - memoryBefore)/1000).ToString(), "n") + "kb"); 
} 

public static void TestReadingFile() 
{ 
    long memoryBefore = GC.GetTotalMemory(true); 
    StreamReader sr = File.OpenText("c:\\temp\\tempFile.txt"); 

    // Add the file contents to a generic list of strings. 
    List<string> fileContents = new List<string>(); 
    while (!sr.EndOfStream) { 
     fileContents.Add(sr.ReadLine()); 
    } 

    // Check for the string. 
    var stringsFound = 
     from line in fileContents 
     where line.Contains("string to search for") 
     select line; 

    sr.Close(); 
    Console.WriteLine("Found: " + stringsFound.Count()); 

    // Check the memory after when the iterator is not used, and output it to the console. 
    long memoryAfter = GC.GetTotalMemory(false); 
    Console.WriteLine("Memory Used Without Iterator = \t" + 
     string.Format(((memoryAfter - memoryBefore)/1000).ToString(), "n") + "kb"); 

} 
} 

// A custom class that implements IEnumerable(T). When you implement IEnumerable(T), 
// you must also implement IEnumerable and IEnumerator(T). 
public class StreamReaderEnumerable : IEnumerable<string> 
{ 

private string _filePath; 
public StreamReaderEnumerable(string filePath) 
{ 
    _filePath = filePath; 
} 


// Must implement GetEnumerator, which returns a new StreamReaderEnumerator. 
public IEnumerator<string> GetEnumerator() 
{ 
    return new StreamReaderEnumerator(_filePath); 
} 

// Must also implement IEnumerable.GetEnumerator, but implement as a private method. 
private IEnumerator GetEnumerator1() 
{ 
    return this.GetEnumerator(); 
} 
IEnumerator IEnumerable.GetEnumerator() 
{ 
    return GetEnumerator1(); 
} 

} 

// When you implement IEnumerable(T), you must also implement IEnumerator(T), 
// which will walk through the contents of the file one line at a time. 
// Implementing IEnumerator(T) requires that you implement IEnumerator and IDisposable. 
public class StreamReaderEnumerator : IEnumerator<string> 
{ 
private StreamReader _sr; 
public StreamReaderEnumerator(string filePath) 
{ 
    _sr = new StreamReader(filePath); 
} 

private string _current; 
// Implement the IEnumerator(T).Current publicly, but implement 
// IEnumerator.Current, which is also required, privately. 
public string Current 
{ 

    get 
    { 
     if (_sr == null || _current == null) 
     { 
      throw new InvalidOperationException(); 
     } 

     return _current; 
    } 
} 

private object Current1 
{ 

    get { return this.Current; } 
} 
object IEnumerator.Current 
{ 
    get { return Current1; } 
} 

// Implement MoveNext and Reset, which are required by IEnumerator. 
public bool MoveNext() 
{ 

    _current = _sr.ReadLine(); 
    if (_current == null) 
     return false; 
    return true; 
} 

public void Reset() 
{ 
    _sr.DiscardBufferedData(); 
    _sr.BaseStream.Seek(0, SeekOrigin.Begin); 
    _current = null; 
} 
// Implement IDisposable, which is also implemented by IEnumerator(T). 
private bool disposedValue = false; 
public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 
protected virtual void Dispose(bool disposing) 
{ 
    if (!this.disposedValue) 
    { 
     if (disposing) 
     { 
      // Dispose of managed resources. 
     } 
     _current = null; 
     _sr.Close(); 
     _sr.Dispose(); 
    } 

    this.disposedValue = true; 
} 

~StreamReaderEnumerator() 
{ 
    Dispose(false); 
} 
// This example displays output similar to the following: 
//Found: 2 
//Memory Used With Iterator =  33kb 
//Found: 2 
//Memory Used Without Iterator = 206kb 
} 
+1

"IEnumerable (T)를 구현할 때 IEnumerable 및 IEnumerator (T)도 구현해야합니다."- 그저 잘못되었습니다. IEnumerable을 구현해야합니다. 당신은 * 열거 자 인터페이스 중 하나를 구현할 필요가 없으며 C# 2 이후 수동 열거 자 구현을 보는 것은 매우 드뭅니다 (그러나 전례가 없습니다) –

+0

간단히 말해서 IEnumerable : IEnumerable'을 넣으십시오. – Jodrell

+0

@MarcGravell : 콜렉션을 래핑하는 클래스가 자체 GetEnumerator 메소드를 구현하여 기본 콜렉션의 GetEnumerator 메소드에 간단히 연결될 수 있다는 점은 주목할 가치가 있습니다. 이 경우, 랩퍼 유형에는 모든 종류의 열거 자 클래스 구현이있을 필요가 없습니다. – supercat

답변

4

의 필요성은 단순히 때문에 인터페이스 구현 방법이다. .NET 1.1에는 제네릭이 없었기 때문에 IEnumerable에는 일반 지원이없고 object 만 노출됩니다. 이는 복싱을 도입합니다 (컴파일러가 IEnumerable과 독립적 인 foreach에 대한 패턴 기반 구현을 지원하는 이유이기도합니다). C# 2에서는 IEnumerable<T>입니다. 모든 는 따라서,도 지정되지 않은 것으로 반복을 입력 고려하는 것이 유용하다 :

IEnumerable<T> 일반 유형으로 GetEnumerator을 재-선언
IEnumerable<T> : IEnumerable 

. 그러나 이제는 서로 다른 서명을 가진 서로 다른 메소드이기 때문에 두 가지 구현이 필요합니다.