Вопрос про exception
Релиб
Форумы       Участники    Календарь    Кто он-лайн?
Добро пожаловать, гость ( Вход | Регистрация )
        



Вопрос про exception Expand / Collapse
Автор
Сообщение
04.09.2006 13:27
Supreme Being

Supreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme Being

участник
Last Login: 02.05.2008 1:27
Сообщ.: 313, Visits: 2 982
есть следующий код:

try{
      userSvc.CreateUser(user);
}
catch (Exception ex) {
      lblErrorString.Text = "error";                       
}


код метода CreateUser содержит такой код:
     try{
  .....
                int errorCode = (int)errorCodeParam.Value;
                if (errorCode < 0)
                {
                    trans.Rollback();
                    connection.Close();
                    if (errorCode == -1)
                    {
                        throw new DuplicateNameException();
                    }
                    else
                    {
                        throw new Exception("Error creating User");
                        //throw new Exception("The provider has a user assigned to it");
                    }
                }
                else
                {
                    trans.Commit();
                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                string msg = "User Exception : " + ex.Message;
                throw ex;

            }


Вопрос: как правильно сделать так чтобы в первой части кода, вместо lblErrorString.Text = "error";        выдавать сообщение об ошибке именно того exception которое случилось?

Сообщ. #903804
04.09.2006 14:42
Supreme Being

Supreme Being

модератор
Last Login: 04.05.2008 13:32
Сообщ.: 7 240, Visits: 65 445
Этот код является прекрасной иллюстрацией как не надо работать с исключениями.

  1. После вызова CreateUser стоит обработчик глотающий все возможные исключения. Это неправильно. Ни одна программа не в состоянии обработать все возможные исключения. Ловить надо те исключения, которые ты в состоянии обработать. Представим, что во время исполнения возникнет исключение StackOverflowException. Данный код сделает вид, что "ничего серьезного не произошло" и будет пытаться продолжить нормальное исполнение. После StackOverflowException долго он не протянет.
  2. Нет никакого смысла возбуждать исключение, ловить его чтобы добавить "информативное сообщение" и возбуждать его по новой. Два аргумента в защиту этого тезиса:
    1. Исключение это "дорогое удовольствие", которые могут сильно влиять на производительность приложения.
    2. Каждое исключение помнит где его вызвали, что очень важно при отладке. Твой код эту информацию теряет. Точнее источником исключения всегда будет считаться блок catch (Exception) { ... } внутри CreateUser. В данном случае, возможно, это не помеха, но в целом это неправильный подход.
  3. Ты используешь try/catch, но не используешь finally. Почему? Он сюда просто просится, чтобы вставить в него вызов connection.Close().

Раз тебе нужно, точно знать причину исключения, то создай свой класс или иерархию исключений, чтобы обрабатывать только их. Например:

public class CreateUserException : Exception
{
  public CreateUserException()
    : base("Error creating User")
  {
  }
}

public class DuplicateNameException : CreateUserException
{
  private string _userName;
  public string UserName { get { return _userName; } }

  public DuplicateNameException(string userName)
    : base("User with name "+userName+" already exists")
  {
    _userName = userName;
  }
}

CreateUserException описывает общую ошибку создания юзера без ясной причины. DuplicateNameException уже имеет явную причину, ясную из имени.

Код CreateUser мог бы выглядеть примерно так:

try
{
  ...
  int errorCode = (int)errorCodeParam.Value;
  if (errorCode < 0)
  {
    trans.Rollback();
    if (errorCode == -1)
    {
      throw new DuplicateNameException();
    }
    else
    {
      throw new CreateUserException();
    }
  }
  else
  {
    trans.Commit();
  }
}
finally
{
  connection.Close();
}

Код обработки ошибки может выглядеть так:

try
{
  userSvc.CreateUser(user);
}
catch (DuplicateNameException ex)
{
  lblErrorString.Text = "Такой юзер уже существует!";
}
catch (CreateUserException ex)
{
  lblErrorString.Text = ex.Message;
}

Или

try
{
  userSvc.CreateUser(user);
}
catch (CreateUserException ex)
{
  lblErrorString.Text = ex.Message;
}

Советую также прочитать заметку по теме FAQ: Why does FxCop warn against catch(Exception)?.

Сообщ. #903808
04.09.2006 14:58
Supreme Being

Supreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme Being

участник
Last Login: 02.05.2008 1:27
Сообщ.: 313, Visits: 2 982
Bazil, спасибо за очень подробный ответ.

finally я использую, просто тут не писал, а вот про кучу исключений - мне это тоже не нравится, но как сказало начальство так и приходится делать.

Проверку я сделал так:

if( ex.GetType().Name=="Duplicate..."){

lblError.Text="...";

}

else{

...

}

Сообщ. #903810
04.09.2006 15:13
Supreme Being

Supreme Being

модератор
Last Login: 04.05.2008 13:32
Сообщ.: 7 240, Visits: 65 445
californis (04.09.2006)
а вот про кучу исключений - мне это тоже не нравится, но как сказало начальство так и приходится делать.

Не понял. Что именно тебе сказали сделать?

californis (04.09.2006)
Проверку я сделал так:

if( ex.GetType().Name=="Duplicate..."){

lblError.Text="...";

}

else{

...

}

Это тоже плохой метод. 1) GetType() работает через рефлексию, что требует от кода наличия привилегий на ее использование и требует времени. 2) Это плохо читабельно и трудно поддерживается 3) Ты лишаешься поддержки со стороны компилятора, в случае ошибки в имени класса исключения. Лучше явно ловить нужные исключения указывая его имя в блоке catch.

Сообщ. #903811
04.09.2006 15:49
Supreme Being

Supreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme Being

участник
Last Login: 02.05.2008 1:27
Сообщ.: 313, Visits: 2 982
bazile (04.09.2006)
californis (04.09.2006)
а вот про кучу исключений - мне это тоже не нравится, но как сказало начальство так и приходится делать.

Не понял. Что именно тебе сказали сделать?

у нас на проекте есть сходные части кода, так вот начальство решает как они должны выглядить. и выглядить они должны:

1) одинаково

2) сделаны так как скажет начальство

californis (04.09.2006)
Проверку я сделал так:

if( ex.GetType().Name=="Duplicate..."){

lblError.Text="...";

}

else{

...

}

Это тоже плохой метод. 1) GetType() работает через рефлексию, что требует от кода наличия привилегий на ее использование и требует времени. 2) Это плохо читабельно и трудно поддерживается 

3) Ты лишаешься поддержки со стороны компилятора, в случае ошибки в имени класса исключения. Лучше явно ловить нужные исключения указывая его имя в блоке catch.[/quote]

 

эта часть кода находится в try-catch.

Сообщ. #903816
04.09.2006 16:02
Supreme Being

Supreme Being

модератор
Last Login: 04.05.2008 13:32
Сообщ.: 7 240, Visits: 65 445
californis (04.09.2006)
у нас на проекте есть сходные части кода, так вот начальство решает как они должны выглядить. и выглядить они должны:

1) одинаково

2) сделаны так как скажет начальство

Это понятно. Меня интересует, что конкретно требует от вас начальство в плане работы с исключениями.

californis (04.09.2006)
эта часть кода находится в try-catch.

Видимо я недостаточно ясно выразился. Представь, что в какой-то момент времени ты решишь убрать класс DuplicateNameException из программы. В случае, использования catch (DuplicateNameException) возникнет ошибка компиляции так как класс DuplicateNameException не существует. Код с проверкой на имя класса if( ex.GetType().Name=="Duplicate...") { } откомпилируется без проблем. На мой взгляд, это явный признак плохо написанного кода.

Или такой стиль и есть одно из требований?

Сообщ. #903817
04.09.2006 16:23
Supreme Being

Supreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme Being

участник
Last Login: 02.05.2008 1:27
Сообщ.: 313, Visits: 2 982

Меня интересует, что конкретно требует от вас начальство в плане работы с исключениями.

допустим есть некая сходная часть кода которая проверяет ту же функциональность. и там написано, например:
 catch (Exception ex)
            {
                string msg = "User Exception : " + ex.Message;
                throw ex;

            }
 
значит в своей части кода, обязательно надо написать точно также:
 catch (Exception ex)
            {
                string msg = "User Exception : " + ex.Message;
                throw ex;

            }



Видимо я недостаточно ясно выразился. Представь, что в какой-то момент времени ты решишь убрать класс DuplicateNameException из программы. В случае, использования catch (DuplicateNameException) возникнет ошибка компиляции так как класс DuplicateNameException не существует. Код с проверкой на имя класса if( ex.GetType().Name=="Duplicate...") { } откомпилируется без проблем. На мой взгляд, это явный признак плохо написанного кода.

Или такой стиль и есть одно из требований?


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


подробнее:
есть код:

try{
      userSvc.CreateUser(user);
}
catch (Exception ex) {
      lblErrorString.Text = "error";                       
}

есть класс и метод вызываемый:
 try{
  .....
                int errorCode = (int)errorCodeParam.Value;
                if (errorCode < 0)
                {
                    trans.Rollback();
                    connection.Close();
                    if (errorCode == -1)
                    {
                        throw new DuplicateNameException();
                    }
                    else
                    {
                        throw new Exception("Error creating User");
                    }
                }
                else
                {
                    trans.Commit();
                    connection.Close();
                }
            }
            catch (Exception ex)
            {
                string msg = "User Exception : " + ex.Message;
                throw ex;

            }


есть также класс для DuplicateNameException:
public class DuplicateNameException : Exception
{
 public DuplicateNameException()
        : base()
 {
 }

    public DuplicateNameException(string message)
        : base(message)
    {
    }

    public DuplicateNameException(string message, Exception innerException)
        : base (message, innerException)
    {
    }

}

Сообщ. #903818
04.09.2006 16:31
Supreme Being

Supreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme BeingSupreme Being