Pranav Sharma
Pranav Sharma

Reputation: 33

implement ienumerable with ienumerable<T>

What is the need to implement IEnumerable (Non-generic) with IEnumerable (Generic) interface in a generic collection class?
A code example on msdn states (Link - http://msdn.microsoft.com/en-us/library/9eekhta0(v=vs.110).aspx)

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
}

Upvotes: 2

Views: 1205

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064184

The need is simply because of how the interfaces are implemented. In .NET 1.1, there were no generics, so IEnumerable has no generic support and only exposes object. This introduces boxing (and is why the compiler also supports a pattern-based implementation for foreach, independent of IEnumerable). In C# 2, we got IEnumerable<T>. It is useful to consider all typed iterations as also being untyped, hence:

IEnumerable<T> : IEnumerable

where IEnumerable<T> re-declares GetEnumerator as with the generic type. However, since these are now different methods with different signatures, it needs 2 different implementations.

Upvotes: 5

Related Questions