Bug in the File.ReadLines(..) method of the .net framework 4.0

后端 未结 7 1363
渐次进展
渐次进展 2021-01-11 16:31

This code :

IEnumerable lines = File.ReadLines(\"file path\");
foreach (var line in lines)
{
    Console.WriteLine(line); 
}
foreach (var line          


        
相关标签:
7条回答
  • 2021-01-11 16:33

    If you need to access the lines twice you can always buffer them into a List<T>

    using System.Linq;
    
    List<string> lines = File.ReadLines("file path").ToList(); 
    foreach (var line in lines) 
    { 
        Console.WriteLine(line);  
    } 
    foreach (var line in lines) 
    {  
        Console.WriteLine(line);  
    } 
    
    0 讨论(0)
  • 2021-01-11 16:34

    I know this is old, but i actually just ran into this while working on some code on a Windows 7 machine. Contrary to what people were saying here, this actually was a bug. See this link.

    So the easy fix is to update your .net framefork. I thought this was worth updating since this was the top search result.

    0 讨论(0)
  • 2021-01-11 16:34

    I don't think it's a bug, and I don't think it's unusual -- in fact that's what I'd expect for something like a text file reader to do. IO is an expensive operation, so in general you want to do everything in one pass.

    0 讨论(0)
  • 2021-01-11 16:35

    It isn't a bug. But I believe you can use ReadAllLines() to do what you want instead. ReadAllLines creates a string array and pulls in all the lines into the array, instead of just a simple enumerator over a stream like ReadLines does.

    0 讨论(0)
  • 2021-01-11 16:38

    I believe you are confusing an IQueryable with an IEnumerable. Yes, it's true that IQueryable can be treated as an IEnumerable, but they are not exactly the same thing. An IQueryable queries each time it's used, while an IEnumerable has no such implied reuse.

    A Linq Query returns an IQueryable. ReadLines returns an IEnumerable.

    There's a subtle distinction here because of the way an Enumerator is created. An IQueryable creates an IEnumerator when you call GetEnumerator() on it (which is done automatically by foreach). ReadLines() creates the IEnumerator when the ReadLines() function is called. As such, when you reuse an IQueryable, it creates a new IEnumerator when you reuse it, but since the ReadLines() creates the IEnumerator (and not an IQueryable), the only way to get a new IEnumerator is to call ReadLines() again.

    In other words, you should only be able to expect to reuse an IQueryable, not an IEnumerator.

    EDIT:

    On further reflection (no pun intended) I think my initial response was a bit too simplistic. If IEnumerable was not reusable, you couldn't do something like this:

    List<int> li = new List<int>() {1, 2, 3, 4};
    
    IEnumerable<int> iei = li;
    
    foreach (var i in iei) { Console.WriteLine(i); }
    foreach (var i in iei) { Console.WriteLine(i); }
    

    Clearly, one would not expect the second foreach to fail.

    The problem, as is so often the case with these kinds of abstractions, is that not everything fits perfectly. For example, Streams are typically one-way, but for network use they had to be adapted to work bi-directionally.

    In this case, an IEnumerable was originally envisioned to be a reusable feature, but it has since been adapted to be so generic that reusability is not a guarantee or even should be expected. Witness the explosion of various libraries that use IEnumerables in non-reusable ways, such as Jeffery Richters PowerThreading library.

    I simply don't think we can assume IEnumerables are reusable in all cases anymore.

    0 讨论(0)
  • 2021-01-11 16:43

    I don't know if it can be considered a bug or not if it's by design but I can certainly say two things...

    1. This should be posted on Connect, not StackOverflow although they're not going to change it before 4.0 is released. And that usually means they won't ever fix it.
    2. The design of the method certainly appears to be flawed.

    You are correct in noting that returning an IEnumerable implies that it should be reusable and it does not guarantee the same results if iterated twice. If it had returned an IEnumerator instead then it would be a different story.

    So anyway, I think it's a good find and I think the API is a lousy one to begin with. ReadAllLines and ReadAllText give you a nice convenient way of getting at the entire file but if the caller cares enough about performance to be using a lazy enumerable, they shouldn't be delegating so much responsibility to a static helper method in the first place.

    0 讨论(0)
提交回复
热议问题