Evr*_*glu 7 .net c# ienumerable
我正在实现我自己的可枚举类型.重新安排的东西:
public class LineReaderEnumerable : IEnumerable<string>, IDisposable
{
private readonly LineEnumerator enumerator;
public LineReaderEnumerable(FileStream fileStream)
{
enumerator = new LineEnumerator(new StreamReader(fileStream, Encoding.Default));
}
public IEnumerator<string> GetEnumerator()
{
return enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Dispose()
{
enumerator.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
枚举器类:
public class LineEnumerator : IEnumerator<string>
{
private readonly StreamReader reader;
private string current;
public LineEnumerator(StreamReader reader)
{
this.reader = reader;
}
public void Dispose()
{
reader.Dispose();
}
public bool MoveNext()
{
if (reader.EndOfStream)
{
return false;
}
current = reader.ReadLine();
return true;
}
public void Reset()
{
reader.DiscardBufferedData();
reader.BaseStream.Seek(0, SeekOrigin.Begin);
reader.BaseStream.Position = 0;
}
public string Current
{
get { return current; }
}
object IEnumerator.Current
{
get { return Current; }
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:我应该在调用GetEnumerator()时调用枚举器上的Reset(),还是调用方法(如foreach)的责任?
GetEnumerator()应该创建一个新的,还是应该总是返回相同的实例?
您的模型从根本上被打破 - 您应该IEnumerator<T>每次GetEnumerator()调用时创建一个新模型.迭代器意味着彼此独立.例如,我应该能够写:
var lines = new LinesEnumerable(...);
foreach (var line1 in lines)
{
foreach (var line2 in lines)
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
并且基本上得到文件中每一行与每个其他行的交叉积.
这意味着LineEnumerable类应不给予FileStream-它应该给予一些东西,可以用来获得一个FileStream你需要一个各一次,如文件名.
例如,您可以使用迭代器块在单个方法调用中完成所有这些操作:
// Like File.ReadLines in .NET 4 - except that's broken (see comments)
public IEnumerable<string> ReadLines(string filename)
{
using (TextReader reader = File.OpenText(filename))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
var lines = ReadLines(filename);
// foreach loops as before
Run Code Online (Sandbox Code Playgroud)
......那会很好.
编辑:请注意,某些序列只能自然迭代一次 - 例如网络流,或来自未知种子的随机数序列.
这样的序列实际上更好地表达IEnumerator<T>而不是IEnumerable<T>,但这使得LINQ的过滤等更难.IMO这样的序列至少应该在第二次调用时抛出异常GetEnumerator()- 两次返回相同的迭代器是一个非常糟糕的主意.
您的类型的用户的期望是GetEnumerator()返回新的枚举器对象.
正如您已定义的那样,每次调用都会GetEnumerator返回相同的枚举器,因此代码如下:
var e1 = instance.GetEnumerator();
e1.MoveNext();
var first = e1.Value();
var e2 = instance.GetEnumerator();
e2.MoveNext();
var firstAgain = e2.Value();
Debug.Assert(first == firstAgain);
Run Code Online (Sandbox Code Playgroud)
不会按预期工作.
(内部调用Reset将是一个不寻常的设计,但这是次要的.)
附加: PS 如果你想要一个文件行的枚举器然后使用File.ReadLines,但它出现(见Jon Skeet答案的评论)这会遇到与你的代码相同的问题.
| 归档时间: |
|
| 查看次数: |
3428 次 |
| 最近记录: |