"yield return"语句不能出现在try/catch块约束中。

3

这两种方法实际上是相同的,但第一种无法编译。我无法弄清楚为什么存在这个限制。

    /// <summary>
    /// Dynamically loads all document extractors from implementation assemblies into an enumeration
    /// </summary>
    private static IEnumerable<IDocumentExtractor> EnumerateInstances()
    {
        IEnumerable<Type> types = EnumerateTypes();

        foreach(Type type in types)
        {
            try
            {
                IDocumentExtractor extractor = Activator.CreateInstance(type) as IDocumentExtractor;
                yield return extractor;
            }
            catch
            {
                _log.WarnFormat("Type {0} couldn't be instanced.", type.Name);
            }
        }
    }

以下是可以成功编译的版本:

    /// <summary>
    /// Dynamically loads all document extractors from implementation assemblies into an enumeration
    /// </summary>
    private static IEnumerable<IDocumentExtractor> EnumerateInstances()
    {
        IEnumerable<Type> types = EnumerateTypes();

        foreach (Type type in types)
        {
            IDocumentExtractor extractor = null;
            try
            {
                extractor = Activator.CreateInstance(type) as IDocumentExtractor;
            }
            catch
            {
                _log.WarnFormat("Type {0} couldn't be instanced.", type.Name);
            }

            if (extractor != null)
                yield return extractor;
        }
    }
4个回答

9

Eric Lippert在他的迭代器块博客系列中详细解释了这一点。从底部开始,逐步向上,不要跳过任何内容,直到找到正确的部分。

我不会在此之外尝试解释它 - 但我会引用第5篇文章的一部分,它实际上讨论了具有catch块的try块(第4篇文章讨论了catch块本身,但那是另一回事)。

所以,如果try块有catch怎么办?
C# 2.0的原始设计者(请记住,这是在我加入团队之前很久)对此进行了激烈的辩论。当我给他们发送电子邮件询问该决策的理由时,这场辩论被缩小了。有三种基本立场:
1.根本不允许在try块中使用yield返回。(或者是像using块这样的语法糖块。)使用一些其他机制而不是“finally”来表达“这是当调用方关闭枚举时运行的代码”的思想。
2.允许在所有try块中使用yield返回。
3.允许在有finally块的try块中使用yield返回,但如果有catch块,则不允许。
(3)的缺点是规则似乎是武断和奇怪的,直到你读了五篇不必要的冗长博客文章才能明白设计团队在想什么。
显然,他们选择了(3),现在你知道为什么了。

1
使用块是try块吗?是这样吗?我从来不知道。 - bevacqua
1
@bevacqua - using 块在 IL 中生成一个 try/finally。它们是等效的。您可以使用 ILDASM 自行查看。 - Dave Black

6

这是关于catch的,而不是try。Try在第5部分中 - 请参见我的答案。 - Jon Skeet
这些链接现在已经失效。请参考Jon Skeet的回答,其中引用了相关部分(虽然它基本上是说“阅读这些博客的全部内容”,所以如果有人找到新链接会很方便)。 - Graham
@Graham 我相信这是至少几篇文章的链接。我只看到了1、2、5和6,但它们似乎来自同一位作者,在同一时间段内发布了相同的标题。https://learn.microsoft.com/en-us/archive/blogs/ericlippert/iterator-blocks-part-five-push-vs-pull - David Jacobsen

0

但你可以写成:

private static IEnumerable<IDocumentExtractor> EnumerateInstances()
{
    IEnumerable<Type> types = EnumerateTypes();

    foreach(Type type in types)
    {
        var oResult = Test(type);
        if (oResult != null)
        {
            yield return oResult;
        }
    }
}

private static IDocumentExtractor Test(Type type)
{
    try
    {
        IDocumentExtractor extractor = Activator.CreateInstance(type) as IDocumentExtractor;
         return extractor;
    }
    catch
    {
        return null;
        //_log.WarnFormat("Type {0} couldn't be instanced.", type.Name);
    }
}

只是 Visual Studio 不想做这个工作,所以你必须自己动手(懒惰的编译器)


-2
   public static IEnumerable IListFind(IEnumerable list, ConditionHandler handler, EventHandler errorHandler = null, DateTime? started = null)
    {
        try
        {
            if (started == null) { started = DateTime.Now; };
            return IListFindInternal(list, handler);
        }
        catch
        {
            if (DateTime.Now.Subtract(started.Value).TotalSeconds < 30)
            {
                if (errorHandler != null) { errorHandler(list, EventArgs.Empty); };
                return IListFind(list, handler, errorHandler, started);
            }
            else
            {
                return null;
            }
        }
    }

    public static IEnumerable IListFindInternal(IEnumerable list, ConditionHandler handler) { foreach (object item in list) { if (handler(item)) { yield return item; } } }

1
请您解释一下您的代码转储做了什么以及 TS 哪里出了问题? - Styxxy

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