如何解决GetEnumerator延迟调用的问题?

3

我有一个简单的代码,意图返回一个枚举器,如果文件不存在或其中的数据版本过时,则返回数据库。很简单。但是我不确定这个方法如何运作。原因如下:

public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
    try
    {
        return GetFromFiles(); // returns instantly here
    }
    catch (ArgumentOutOfRangeException)
    {
        return GetFromBackingStore();
    }
    catch (FileNotFoundException)
    {
        return GetFromBackingStore();
    }
}

GetFromFiles 看起来像这样:

private IEnumerator<KeyValuePair<TKey, TValue>> GetFromFiles()
{
    foreach (var path in _paths)
    {
        using (var fs = _versioner.TryOpen(path))
        {
            var reader = _serializerFactory.CreateSerializer(fs, FileAccess.Read);

            KeyValuePair<TKey, TValue> pair;
            while (reader.Read(out pair))
            {
                yield return pair;
            }
        }
    }
}

现在问题是,当使用foreach调用GetEnumerator时,它会立即返回而不执行GetFromFiles的调用,但随后它会回到GetFromFiles方法,但现在try-catch已经不起作用了,所以如果TryOpen抛出异常,则无法处理异常。我正在尝试理解原因以及如何解决它。我认为这一定与yield return有关,如果是这种情况,是否有方法可以解决问题?
3个回答

2
迭代器块具有延迟执行的特点。在某些情况下,您可以重构(“提取方法”)迭代器块的核心部分,以便立即执行核心测试并仅推迟迭代,但是在此处不可能进行此操作,因为您正在执行外部foreach(特别是,除非您打开所有文件,否则只能真正地预先检查第一个文件)。
也许最简单的方法(如果大小适中)是添加.ToList()以立即完成整个操作:
return GetFromFiles().ToList();

2
第一种方法包括try/catch,它返回对第二种方法的引用,因此只会调用一次。每次外部代码枚举时,它不会再次调用第一种方法,因为它已经有了一个枚举器的引用,因此直接调用它。您需要将try/catch移动到第二个方法中,或者使用标准集合一次性获取所有初始化而不是逐个读取元素。希望这可以帮助您。

2
答案在于yield操作符以及try catch中没有执行枚举器。这部分代码:

try { return GetFromFiles(); // 只是一个承诺 }

返回了一个IEnumerable<>的枚举器作为一个承诺。目前还没有对其进行显式迭代。而yield则充当了一个未来。当调用.ToList()或.Count()时,它将会被执行...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接