JMK
JMK

Reputation: 28069

Handling Casting exceptions in a foreach statement

Ok, lets say I have an array of objects in C# .Net like so:

object[] myObjects = new object[9];
myObjects[0] = "Foo";
myObjects[1] = 3;
myObjects[2] = 2.75;
myObjects[3] = "Bar";
myObjects[4] = 675;
myObjects[5] = "FooBar";
myObjects[6] = 12;
myObjects[7] = 11;
myObjects[8] = "FooBarFooBar";

I want to, inside a foreach block, enumerate this array and write every string to a text document using StreamWriter like so:

StreamWriter sw = new StreamWriter(@"C:\z\foobar.txt");

foreach(string myObject in myObjects)
{
    sw.WriteLine(myObject);
}

sw.Flush();
sw.Close();

My problem is that whenever I try to cast the integers and doubles to String, an exception will be thrown.

If I put a try/catch block around my foreach statement, the exception that gets thrown on the second iteration will trigger the catching of the exception and nothing will get written to my text document.

Putting the try/catch inside the foreach is pointless because the exception happens on the cast.

I want to use a foreach loop (let's assume that for loops don't exist, and that we can't use indexing or ToString()) to enumerate an array of objects, casting each to a string and writing these to a text document using StreamWriter. If the cast works, happy days. If not, I want to catch the exception thrown and continue to enumerate the remaining objects.

Thanks

Edit: Before somebody says it, this isn't homework! I am trying to solve a real world problem.

Upvotes: 3

Views: 2891

Answers (4)

Prashanth Thurairatnam
Prashanth Thurairatnam

Reputation: 4361

OK. I know that the question has been answered. But I thought this is interesting. If you want to really get hold of all the exceptions thrown within a foreach loop you could probably store it as a Generic List of type Exception and later you can throw it as an AggregateException

more info in this blog post and this MSDN link

Here is my implementation corresponding to your scenario (this will capture all exceptions thrown within the foreach loop and still the loop won't break until the object array is fully looped)

        try
        {
            List<Exception> exceptions = null;
            foreach (object myObject in myObjects)
            {                    
                try
                {
                    string str = (string)myObject;
                    if (str != null)
                    {
                        sw.WriteLine(str);
                    }
                }
                catch (Exception ex)
                {
                    if (exceptions == null)
                        exceptions = new List<Exception>();
                    exceptions.Add(ex);
                }
            }

            if (exceptions != null) 
                throw new AggregateException(exceptions);                
        }
        catch(AggregateException ae)
        {
            //Do whatever you want with the exception or throw it
            throw ae;
        }   

Upvotes: 0

Bob Horn
Bob Horn

Reputation: 34325

Can't you just catch and continue?

StreamWriter sw = new StreamWriter(@"C:\z\foobar.txt");

foreach(string myObject in myObjects)
{
    try
    {
      sw.WriteLine(myObject);
    }
    catch (Exception ex)
    {
      // process exception here
      continue;
    }
}

sw.Flush();
sw.Close();

Upvotes: 0

Michael Liu
Michael Liu

Reputation: 55469

Because you expect a heterogeneous collection, it's better to avoid throwing InvalidCastException in the first place. Read up on "boneheaded exceptions" in Eric Lippert's excellent Vexing exceptions article.

Option 1: Use the LINQ OfType<TResult>() extension method to pick out only elements of a specified type:

// using System.Linq;

foreach(string myObject in myObjects.OfType<string>())
{
    sw.WriteLine(myObject);
}

Option 2: Do the type check yourself:

foreach(object myObject in myObjects)
{
    string s = myObject as string;
    if (s != null)
        sw.WriteLine(s);
}

This option is easy to extend to handle multiple types.

UPDATE:

Ok, but what would happen if, in some outlandish scenario, an exception was still thrown on this line. Is there a way to handle the exception and then continue with my enumeration?

Here are the other ways an exception could be thrown on the foreach line, none of which you can sensibly handle:

  • myObjects.GetEnumerator() throws an exception. In this case, the enumeration can't be started at all.
  • IEnumerator<string>.MoveNext() throws an exception. In this case, the enumerator is likely to be corrupt, and enumeration cannot continue.
  • Some other type of fatal exception occurs: OutOfMemoryException, StackOverflowException, etc. In this case, you should just let the process die.

Upvotes: 6

Helstein
Helstein

Reputation: 330

You don't need to cast the object to string yourself. StreamWriter has an overload that takes object and does the casting automatically (MSDN link):

foreach (object myObject in myObjects)
{
    sw.WriteLine(myObject);
}

Upvotes: 0

Related Questions