3

These two methods are practically the same, yet the first one can't compile. I can't figure out the reason this constraint exists

    /// <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);
            }
        }
    }

And the version that actually compiles without issues:

    /// <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;
        }
    }
bevacqua
  • 47,502
  • 56
  • 171
  • 285

4 Answers4

9

Eric Lippert explains this in great detail in his blog series about iterator blocks. Start at the bottom and work your way up, and don't skip anything until you've reached the right bit.

I'm not going to try to explain it beyond that - but I'll quote part of post 5, which is actually the one which talks about try blocks which have catch blocks (post 4 talks about catch blocks themselves, but that's a different matter).

So what if the try block has a catch?

The original designers of C# 2.0 -- and remember, this is long before my time on the team -- had a huge debate over this. A debate which was repeated in miniature when I sent them all an email asking for the justification for this decision. There are three basic positions:

  1. Do not allow yield returns in try blocks at all. (Or blocks that are sugars for try blocks, like using blocks.) Use some other mechanism other than "finally" to express the idea of "this is the code that you run when the caller shuts down the enumeration."

  2. Allow yield returns in all try blocks.

  3. Allow yield returns in try blocks that have finally blocks, but not if they have catch blocks.

[snip]

The down side of (3) is that the rule seems arbitrary and weird -- until you read five unnecessarily prolix blog entries that explain what on earth the design team was thinking.

Obviously, they picked (3), and now you know why.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
6

You can get the answer to this question in Eric Lippert's series on Iterator Blocks. Specifically check out the post titled Iterator Blocks, Part Five: Push vs Pull. The seven-part series starts with Iterator Blocks, Part One.

jason
  • 236,483
  • 35
  • 423
  • 525
  • That's about catch, not about try. Try is in part 5 - see my answer. – Jon Skeet Nov 03 '11 at 14:28
  • The links are now dead. See [Jon Skeets answer](https://stackoverflow.com/a/7996570/1128762) which quotes the relevant part (though it essentially says "read the whole of these blogs" so it'd be handy if someone finds new links) – Graham Feb 03 '21 at 12:31
  • @Graham I believe this is a link to at least a few of the posts. I only see 1, 2 and 5 and 6 but it appears to be from the same author, title and time period of posting. https://learn.microsoft.com/en-us/archive/blogs/ericlippert/iterator-blocks-part-five-push-vs-pull – David Jacobsen Mar 14 '22 at 14:25
0

But you can write :

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);
    }
}

It's just that Visual studio doesn't want to do this job, so you have to do it yourself (lazy compiler)

Toto
  • 149
  • 2
-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; } } }
Basil
  • 15
  • 2