Обработка исключений приведения в операторе foreach

Хорошо, скажем, у меня есть массив объектов в C# .Net вот так:

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";

Я хочу внутри блока foreach перечислить этот массив и записать каждую строку в текстовый документ с помощью StreamWriter следующим образом:

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

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

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

Моя проблема в том, что всякий раз, когда я пытаюсь преобразовать integers и doubles в String, возникает исключение.

Если я помещу блок try/catch вокруг моего оператора foreach, исключение, которое будет выброшено на второй итерации, вызовет перехват исключения, и ничего не будет записано в мой текстовый документ.

Помещать try/catch внутрь foreach бессмысленно, потому что исключение происходит при приведении.

Я хочу использовать цикл foreach (предположим, что циклов for не существует и что мы не можем использовать индексирование или ToString()) для перечисления массива объектов, приведения каждого к строке и записи их в текстовый документ с использованием StreamWriter . Если бросок работает, счастливых дней. Если нет, я хочу перехватить выброшенное исключение и продолжить перечисление оставшихся объектов.

Спасибо

Редактировать: Пока кто-то не сказал, это не домашнее задание! Я пытаюсь решить проблему реального мира.


person JMK    schedule 20.05.2012    source источник


Ответы (4)


Поскольку вы ожидаете разнородную коллекцию, лучше вообще не выбрасывать InvalidCastException. Почитайте о «тупоголовых исключениях» в отличном Эрика Липперта. Досадные исключения.

Вариант 1. Используйте расширение LINQ OfType<TResult>(). метод для выбора только элементов указанного типа:

// using System.Linq;

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

Вариант 2. Проведите проверку типов самостоятельно:

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

Эту опцию легко расширить для обработки нескольких типов.

ОБНОВЛЕНИЕ:

Хорошо, но что произойдет, если в каком-то диковинном сценарии в этой строке все же будет выброшено исключение. Есть ли способ обработать исключение, а затем продолжить перечисление?

Вот другие способы, которыми может быть выброшено исключение в строке foreach, ни один из которых вы не можете разумно обработать:

  • myObjects.GetEnumerator() выдает исключение. В этом случае перечисление вообще не может быть запущено.
  • IEnumerator<string>.MoveNext() выдает исключение. В этом случае перечислитель, вероятно, поврежден, и перечисление не может быть продолжено.
  • Возникает какой-то другой тип фатального исключения: OutOfMemoryException, StackOverflowException и т. д. В этом случае вы должны просто позволить процессу умереть.
person Michael Liu    schedule 20.05.2012
comment
Хорошо, но что произойдет, если в каком-то диковинном сценарии в этой строке все же будет выброшено исключение. Есть ли способ обработать исключение, а затем продолжить перечисление? - person JMK; 20.05.2012
comment
@JMK, вы не должны обрабатывать исключения, которых вы не знаете. Если вы попадете в «диковинный сценарий», которого вы не ожидали, единственная безопасная вещь — это разбить приложение, не обработав исключение. - person svick; 20.05.2012
comment
@JMK: смотрите мой обновленный ответ. По сути, я не думаю, что вам нужно беспокоиться об этом сценарии, потому что все способы, которыми может быть выдано исключение, являются неисправимыми условиями. - person Michael Liu; 20.05.2012
comment
Хорошо, спасибо, вы очень помогли! Гораздо больше, чем утка! - person JMK; 20.05.2012

Вам не нужно приводить объект к строке самостоятельно. StreamWriter имеет перегруженную версию, которая принимает объект и автоматически выполняет преобразование (ссылка MSDN ):

foreach (object myObject in myObjects)
{
    sw.WriteLine(myObject);
}
person Helstein    schedule 20.05.2012

Разве ты не можешь просто поймать и продолжить?

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();
person Bob Horn    schedule 20.05.2012
comment
Ошибка в foreach (строка myObject в myObjects), а не в sw.WriteLine (myObject); грустно! - person JMK; 20.05.2012
comment
Хорошо, так почему бы не использовать foreach (объект myObject в myObjects)? - person Bob Horn; 20.05.2012
comment
Я все еще получаю ошибки. Некоторые данные, с которыми я работаю, повреждены, но у меня нет выбора! Иногда foreach (объект e в de.Properties.Values) просто генерирует исключение (Visual Studio сообщает, что это неизвестное исключение), но мне все еще нужно работать с остальными данными, поэтому я искал способ обработки исключений. бросается в эту строку, не прерывая цикл foreach. - person JMK; 20.05.2012
comment
Хорошо. Я думаю, что некоторая путаница заключается в том, что вы пытались сделать это более простым примером, чем то, с чем вы действительно имеете дело, и некоторые комментарии и ответы работают для вашего простого сценария, но не для вашего реального сценария. Ответ Майкла выглядит хорошим. Удачи! - person Bob Horn; 20.05.2012
comment
Я так думаю, извините за путаницу и спасибо за попытку помочь! - person JMK; 20.05.2012
comment
Неизвестное исключение звучит как серьезная проблема. Является ли приложение 100% кодом .NET или есть собственный код, который может вызывать исключения? (Возможно, вам следует задать новый вопрос об этом.) - person Michael Liu; 20.05.2012
comment
Этот вопрос следует из моего последнего вопроса. но я решил, что этой проблемы самой по себе достаточно, поэтому я задал отдельный вопрос, а не редактировал его. Я имею дело с данными LDAP. Мой код полностью C# .Net, но устройство с базой данных LDAP находится в системе Unix, которая является частью нашей телефонной системы. - person JMK; 20.05.2012

ХОРОШО. Я знаю, что на вопрос ответили. Но я подумал, что это интересно. Если вы действительно хотите получить все исключения, созданные в цикле foreach, вы, вероятно, могли бы сохранить его как общий список типа Exception, а позже вы можете бросить его как AggregateException

дополнительную информацию можно найти в этой записи блога и в этой ссылка MSDN

Вот моя реализация, соответствующая вашему сценарию (это будет фиксировать все исключения, созданные в цикле foreach, и все же цикл не прервется, пока массив объектов не будет полностью зациклен)

        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;
        }   
person Prashanth Thurairatnam    schedule 20.05.2012