Главная » C# » Код, не вызывающий исключений в Visual C# (Sharp)

0

Теперь, когда мы знаем, как реализовывать обработчики исключений, рассмотрим еще лучший подход к проблеме исключений: не вызывать их. Мы будем фокусираться на том, как можно сделать код более безопасным и менее склонным к генированию исключений.

Защитный код

К сожалению слишком часто исключения, такие как NuliReferenceException, воикают  потому,  что  разработчики  не  принимают  меры,  чтобы  удостовериться в действительности состояния во фрагменте кода. А недействительное состояние вызовет исключение. Более того, один из примеров в этой главе содержит как раз такую ситуацию, которую вы, наверное, заметили. Вот этот код с его маленьким глупым упущением, которое может вылиться в исключение:

void TestCallingExample() { CallingExample els = null; try {

els – new CallingExample(); els.Method();

}

catch (Exception) { ;}

Console.WriteLine("Depth is (" + cls.GetDepthO + n)");

}

Проблемный участок кода выделен жирным шрифтом, а проблема состоит в том, что  в  нем делается  предположение, что  переменная  els будет всегда обращаться к действительному экземпляру CallingExample. Данное предположение относится к классу предположений, которые мы не можем позволить себе допускать. Если исключение произойдет при создании экземпляра CallingExample, то значение пеменной els останется null, а блок catch перехватит это исключение, таким оазом, предотвращая зависание программы. Но использование метода cls.GetDepthO сразу же после этого сводит всю нашу защиту на нет, т. к. перенная els содержит null, что вызовет исключение NuliReferenceException. Лучше написать этот код так:

void TestCallingExample() { CallingExample els = null; try {

els = new CallingExample(); els.Method();

}

catch (Exception) { ;}

if (els != null) {

Console.WriteLine("Depth is (" + els.GetDepth() + ")");

}

}

В строке, выделенной жирным шрифтом, иллюстрируется защитный код, который проверяет, не содержит ли переменная cis значение null, и если не содержит, то позволяет обращение к методу cis.GetDepthO. Написание кода таким образом делает его защищенным от исключений. Это не означает, что исключения не могут возникнуть совсем, т. к. они могут произойти в методе GetDeptho, но по отношию к методу TestCaliingExample () мы обезопасились, насколько могли, и преолагаем малую вероятность возникновения исключений в методе GetDepth ().

Но в методе TestCaliingExample () отсутствует способ индикации, была ли обротка успешной. Код, вызывающий метод  TestCaliingExample(),  предполагает, что в результате этого вызова всегда будет что-то сделано. Кроме как возникновия исключения в вызываемом методе, вызывающий код не имеет никакой воожности узнать, если что-то в методе TestCaliingExample ()  не сработало.

Код, который сообщает о проблемах с помощью исключения, является одновренно и добром, и злом. Добром потому, что код сообщает о возникших проблах. Злом потому, что иногда вы знаете, что может возникнуть какая-то некритичкая ошибка, и вы не хотите, чтобы это исключение поднималось наверх иерархии исполнения программы. В таких случаях исключение необходимо перехватить, что вызывает усложнение кода.

Скажем,  необходимо  преобразовать  строковое  представление  числа  собственно в число. Предоставляемые .NET процедуры преобразования обычно выдают рультат в случае успешного выполнения операции, но генерируют исключение, еи что-то идет не так. И только исключение, а не возвращаемое значение или паретр. Но при преобразовании чисел мы знаем, что с такими операциями всегда существует возможность какой-либо ошибки, поэтому для такого случая нам неоодимо предоставить обработчик исключения. Рассмотрим следующий код для преобразования числа:

int TestGetValue(string buffer) { int retval = 0;

try {

retval = int.Parse(buffer);

}

catch (FormatException ex) { Console.WriteLine("Exception (" + ex.Message + ")");

}

return retval;

}

В  данном  примере  код осознает,  что  если  в  вызванном  методе  Parse ()  строкый параметр buffer неправильного формата, например, содержит недопустимые

символы, то будет сгенерировано исключение. Исключение будет перехвачено, оаботано (проблема установлена с помощью свойства Message исключения), после чего значение переменной retval будет возвращено вызывающему коду. Но как исключение повлияет на результаты выполнения последующего кода? Мы видим, что возвращаемая методом parsed переменная retval была инициализирована значением по умолчанию 0. Это действительное число и может быть интерпретовано как результат успешною преобразования.

Это обстоятельство ставит разработчика в затруднительное положение.  11ерехватая  исключение,  метод TestGetvaiueo как бы говорит:  "Я  всегда возвращу вызающему коду действительное значение". Тем не менее, некоторые якобы действельные  возвращаемые  значения  на  деле  не  являются  таковыми.  Это  значения, возвращаемые, когда при преобразовании числа возникает исключение. Таким обром, перехватывая исключение, мы поступаем совсем неправильно, т. к. здесь нужно предоставить задачу перехвата исключения вызывающему коду высшего уровня. Но и здесь не все так просто. Действительно ли мы хотим извещать вызывающий код, что  выполнение  преобразования  является  невозможным?  Возможно,  вызывающий код более заинтересован  в том, действительно ли возвращенное значение.  В таком случае извещение его о внутренних проблемах метода будет сродни докладыванию генеральному директору о том, что в офисе окончились скобки для степлеров. Кечно же,  в соответствующем  контексте скобки для степлеров также важны,  и без них  производительность  компании  может понизиться  на сотую долю  процента,  но действительно ли мы будем докладывать об этой проблеме генеральному директору?

Проблема с преобразованием известна разработчикам Microsoft, и для ее решения они применяют подход, который может пригодиться и нам. Как мы узнали в главе 3, строковое представление число можно преобразовать в собственно число с помью двух методов:

•    метод Parse () возвращает действительное число, если ему передается действельное строковое представление числа; в противном случае этот метод выдает исключение;

П метод TryParse (), кроме преобразованного значения в случае успешного прбразования, также возвращает значение true или false, указывающее на рультат преобразования.

Метод  TestGetvalue <)  можно  модифицировать для  использования  в  нем  метода

TryParse () таким образом:

bool TestGetvalue(string buffer, out int val) { bool retval = false;

if (int.TryParse(buffer, out val)) { retval = true;

}

return retval;

}

В модифицированном примере метод TestGetvalue() возвращает true или false, таким образом указывая успех или неудачу операции преобразования строки в число. Если  возвращается  true, параметр  val будет  указывать  на  допустимое  число;  в противном случае этот параметр не используется.

Некоторые из читателей могли заметить, что способ применения методов Parse () и TryParse () не блещет изобретательностью. А именно метод TestGetvalue () можно свести к одному оператору:

bool TestGetvalue(string buffer, out int val) { return int.TryParse(buffer, out val);

}

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии