0

The following code snippet is meant to catch any exceptions while enumerating and ignore that particular item.

using System;
using System.Linq;
using System.Collections.Generic;

public class Program
{
     public static void Main()
        {
            DataProvider[] dataproviders = new DataProvider[]
            {
                new DataProvider(itemCount: 3, throwAfter: 1),
                new DataProvider(itemCount: 4),
            };
            var output = ListEnumerable(dataproviders).ToList();
            // We'll never get there. I excepted items from second dataprovider here
            Console.WriteLine("Success");
        }
        private static IEnumerable<Item> ListEnumerable(IEnumerable<DataProvider> providers)
        {
            ArgumentException providerException = null;
            foreach (DataProvider provider in providers)
            {
                providerException = null;
                try
                {
                    return provider.GetData();
                }
                catch (ArgumentException ex)
                {
                    // Expected to catch the argumentexception thrown in GetData here. This never happens
                    providerException = ex;
                    Console.WriteLine("Exception caught");
                }
            }
            if (providerException != null)
            {
                throw providerException;
            }
            return Enumerable.Empty<Item>();
        }
        private class DataProvider
        {
            public DataProvider(int itemCount = 3, int throwAfter = -1)
            {
                this.ItemCount = itemCount;
                this.ThrowAfter = throwAfter;
            }
            public int ItemCount { get; }
            public int ThrowAfter { get; }
            public IEnumerable<Item> GetData()
            {
                for (int i = 0; i < this.ItemCount; ++i)
                {
                    if (i == this.ThrowAfter)
                    {
                        throw new ArgumentException("Thrown after " + i);
                    }
                    yield return new Item();
                }
            }
        }
        private class Item
        {
            public string Name = Guid.NewGuid().ToString();
        }
}

I expected the exception to be thrown in ListEnumerable and then go to the next item. But, running the snippet causes the exception to be thrown in main.

Why is this happening? What is the correct way to express this with lazy evaluation of the data still intact?

Vinay Chandra
  • 502
  • 5
  • 18
  • You really should be capturing all the exceptions in a list and then throw an `AggregateException` rather than just throwing the last one caught. – juharr Aug 27 '20 at 15:52
  • `return provider.GetData();` returns an `IEnumerable`. You never enumerate it, so nothing happens. – Dennis_E Aug 27 '20 at 15:55
  • 2
    Your problem is that the code that actually throws will not be run until you iterate it. Just change it to `return provider.GetData().ToList();` and it will throw when you expect. This is why most Linq methods first check for nulls and throw an exception and then have iterator code in a separate method, so that the code is lazy, but the error checking is not. – juharr Aug 27 '20 at 15:55
  • The comments are helpful. Although I understand the request to do `ToList`, in this case, the calculation of `GetData` is very expensive and that is the reason we are trying to lazy evaluate the output. – Vinay Chandra Aug 27 '20 at 16:02
  • 2
    You cannot have it both ways. You want to have lazy evaluation, but you also want it to immediately evaluate to determine if there's an exception. If you can separate the exceptions to be checked up front and immediately throw them, then you can make the rest lazy, but the example you have the exceptions are part of the iteration and cannot be separated. – juharr Aug 27 '20 at 16:06

0 Answers0