C# IAsynceneumber方法中的正确处理?

C# IAsynceneumber方法中的正确处理?,c#,iasyncenumerable,iasyncdisposable,C#,Iasyncenumerable,Iasyncdisposable,这个问题的答案可能是不可能的,但问题是:假设您有一个C方法来使用TextReader中返回IAsyncEnumerable的行。在iasyncumerator上调用DisposeAsync时,如何确保TextReader已被释放?或者这是您需要编写自定义实现才能实现的吗?只需在异步迭代器中使用try-finally块,或者更简单地说是使用块的,调用方完成枚举后,文本阅读器将立即被释放。枚举是正常完成,还是由于异常或中断而提前完成,这无关紧要 static async Task Main(stri

这个问题的答案可能是不可能的,但问题是:假设您有一个C方法来使用
TextReader
中返回
IAsyncEnumerable
的行。在
iasyncumerator
上调用
DisposeAsync
时,如何确保
TextReader
已被释放?或者这是您需要编写自定义实现才能实现的吗?

只需在异步迭代器中使用try-finally块,或者更简单地说是使用
块的
,调用方完成枚举后,
文本阅读器将立即被释放。枚举是正常完成,还是由于异常或
中断而提前完成,这无关紧要

static async Task Main(string[] args)
{
    await foreach (var line in GetLines())
    {
        Console.WriteLine(line);
    }
}

private static async IAsyncEnumerable<string> GetLines()
{
    var reader = new StringReader("Line1\nLine2\nLine3");
    try
    {
        while (true)
        {
            var line = await reader.ReadLineAsync();
            if (line == null) break;
            yield return line;
        }
    }
    finally
    {
        reader.Dispose();
        Console.WriteLine("Disposed");
    }
}
static async Task Main(字符串[]args)
{
等待foreach(GetLines()中的变量行)
{
控制台写入线(行);
}
}
私有静态异步IAsyncEnumerable GetLines()
{
变量读取器=新的StringReader(“Line1\nLine2\nLine3”);
尝试
{
while(true)
{
var line=wait reader.ReadLineAsync();
如果(line==null)中断;
收益率回归线;
}
}
最后
{
reader.Dispose();
控制台。写入线(“已处置”);
}
}
输出:

第1行
第2行
第3行
处置


只需在异步迭代器中使用try finally块,或者更简单地使用
using
块,调用方完成枚举后,
TextReader
就会被释放。枚举是正常完成,还是由于异常或
中断而提前完成,这无关紧要

static async Task Main(string[] args)
{
    await foreach (var line in GetLines())
    {
        Console.WriteLine(line);
    }
}

private static async IAsyncEnumerable<string> GetLines()
{
    var reader = new StringReader("Line1\nLine2\nLine3");
    try
    {
        while (true)
        {
            var line = await reader.ReadLineAsync();
            if (line == null) break;
            yield return line;
        }
    }
    finally
    {
        reader.Dispose();
        Console.WriteLine("Disposed");
    }
}
static async Task Main(字符串[]args)
{
等待foreach(GetLines()中的变量行)
{
控制台写入线(行);
}
}
私有静态异步IAsyncEnumerable GetLines()
{
变量读取器=新的StringReader(“Line1\nLine2\nLine3”);
尝试
{
while(true)
{
var line=wait reader.ReadLineAsync();
如果(line==null)中断;
收益率回归线;
}
}
最后
{
reader.Dispose();
控制台。写入线(“已处置”);
}
}
输出:

第1行
第2行
第3行
处置


实际上,处置
TextReader
并不是枚举的工作,而是创建
TextReader
的人的工作。通常情况下,您会期望代码在自己的
任务中使用自己的
。从理论上讲,您可以将读者传递给枚举器,让它负责,但这将减少它实际有用的场景。从概念上讲,枚举数不同于它们所枚举的内容。请注意,如果您有一个迭代器方法本身创建
TextReader
,那么您所需要做的就是确保它使用var reader=new TextReader(…)
;编译器魔术将迭代器的使用块转换为dispose上调用的东西,就像非异步迭代器一样。实际上,处置
TextReader
不是枚举的工作,而是创建
TextReader
的任何人的工作。通常情况下,您会期望代码在自己的
任务中使用自己的
。从理论上讲,您可以将读者传递给枚举器,让它负责,但这将减少它实际有用的场景。从概念上讲,枚举数不同于它们所枚举的内容。请注意,如果您有一个迭代器方法本身创建
TextReader
,那么您所需要做的就是确保它使用var reader=new TextReader(…)
;编译器魔术将迭代器的using块转换为dispose上调用的内容,就像非异步迭代器一样。如果foreach是一个永不结束的无限循环,则不会释放枚举器,也不会调用finally块。这是我能想到的唯一一种情况。另一种情况是,如果一个有缺陷或恶意的调用方(通过调用该方法)创建了一个枚举数,然后在静态字段中保留对它的引用,以防止它被垃圾收集。这种可能性也适用于普通(非异步)枚举器/枚举器。我想的实际情况是,如果有人因为获得了所需的数据而中断循环。(我不太关心臭虫/恶意。)本案例已涵盖。中断
foreach
循环会导致立即处理循环中使用的枚举数,从而导致在迭代器中调用finally块。如果foreach是一个永不结束的无限循环,则不会处理枚举数,也不会调用finally块。这是我能想到的唯一一种情况。另一种情况是,如果一个有缺陷或恶意的调用方(通过调用该方法)创建了一个枚举数,然后在静态字段中保留对它的引用,以防止它被垃圾收集。这种可能性也适用于普通(非异步)枚举器/枚举器。我想的实际情况是,如果有人因为获得了所需的数据而中断循环。(我不太关心臭虫/恶意。)本案例已涵盖。中断
foreach
循环会立即处理循环中使用的枚举数,从而在迭代器中调用finally块。