【问题标题】:implement ienumerable with ienumerable<T>用 ienumerable<T> 实现 ienumerable
【发布时间】:2014-12-04 01:01:02
【问题描述】:

在泛型集合类中使用 IEnumerable (Generic) 接口实现 IEnumerable (Non-generic) 有什么需要?
关于 msdn 状态的代码示例 (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
}

【问题讨论】:

  • “当你实现 IEnumerable(T) 时,你还必须实现 IEnumerable 和 IEnumerator(T)”——这是错误的;你必须实现 IEnumerable;您不必实现任何一个枚举器接口,并且从 C# 2 开始,很少看到手动枚举器实现(但并非闻所未闻)
  • 简单地说IEnumerable&lt;T&gt; : IEnumerable
  • @MarcGravell:可能值得注意的是,包装集合的类可以实现自己的GetEnumerator 方法,以便它们简单地链接到底层集合的GetEnumerator 方法。在这种情况下,包装器类型不需要任何类型的枚举器类的任何实现。

标签: c# .net generics interface ienumerable


【解决方案1】:

需要仅仅是因为接口是如何实现的。在 .NET 1.1 中,没有泛型,所以 IEnumerable 没有泛型支持,只公开了 object。这引入了装箱(这就是为什么编译器还支持foreach 的基于模式的实现,独立于IEnumerable)。在 C# 2 中,我们得到了IEnumerable&lt;T&gt;。将所有 typed 迭代视为无类型是很有用的,因此:

IEnumerable<T> : IEnumerable

其中IEnumerable&lt;T&gt;GetEnumerator 重新声明为泛型类型。但是,由于这些现在是具有不同签名的不同方法,因此需要两种不同的实现。

【讨论】:

    猜你喜欢
    • 2011-07-29
    • 2012-07-03
    • 2014-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 2011-01-09
    相关资源
    最近更新 更多