Обработка исключений и регистрация в C# - PullRequest
2 голосов
/ 29 апреля 2020

Допустим, у меня есть класс Person с различными атрибутами разных типов данных, и у меня есть этот код в моем основном методе:

Person p1 = new Person
    {
        Name = inputName,
        DateOfBirth = inputDOB,
        Height = height,
        Weight = weight,
        Passport = inputPassport
    };

Теперь, скажем, присвоение высоты генерирует исключение, например InvalidCastException, код прекращает выполнение и, вероятно, регистрирует что-то в случае, если у меня есть таблица журналирования. Проблема заключается в том, что из журнала я не смогу точно понять, какая строка и назначение выдают исключение.

Есть ли способ перехватить исключение и войти в систему (где-нибудь в базе данных) какое назначение вызывает исключение и для какого типа приведения в данном случае (например, двойное для DateTime).


ОБНОВЛЕНИЕ: В конце я решил свою проблему, выполнив это:

  1. Обновление кода, чтобы он сначала создавал объект (без установки свойств), а затем устанавливал свойства по одной строке за раз.
  2. Между каждой строкой добавьте строку, которая объединяет значение только что установленной переменной.
  3. В моем улове я регистрировал строку объединения, поэтому быстрый взгляд на эту строку в основном говорит me, которая была последней строкой кода, которая была успешно выполнена перед выдачей исключения.

1 Ответ

1 голос
/ 30 апреля 2020

Это известная проблема. Вот несколько ссылок, связанных с ним:

Я также проверил ваш сценарий, используя C# 7.3 (.NET 4.7.2) и C# 8 (.NET Core 3.1), и проблема все еще существует. Поэтому, если вы используете инициализатор объекта и одно из значений инициализатора выдает исключение, то из трассировки стека невозможно узнать, какое из значений инициализатора вызвало исключение.


Обходной путь

Давайте рассмотрим следующий класс Person:

class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int Age { get; set; }
}

и предположим, что экземпляр класса Person создается с использованием следующего кода:

// Return type of the methods getName(), getDateOfBirth() and getAge() is object.
// Each of this methods can return invalid value, for example, method getAge()
// can return string value causing InvalidCastException.
Person p = new Person
{
    Name = (string) getName(),
    DateOfBirth = (DateTime) getDateOfBirth(),
    Age = (int) getAge()
};

Если одно из значений инициализатора (например, приведение результата метода getName() к string типу данных) выдает исключение, мы не сможем узнать, какое из значений инициализатора является причина исключения, потому что номер строки в трассировке стека будет указывать на строку Person p = new Person.

Чтобы обойти эту проблему, мы можем отказаться от использования инициализатора объекта и использовать инициализацию объекта старого стиля:

Person p = new Person();
p.Name = (string) getName();
p.DateOfBirth = (DateTime) getDateOfBirth();
p.Age = (int) getAge();

Теперь, если возникнет исключение, мы сможем узнать точный номер строки, где произошло исключение.

Здесь мы должны выбрать между чтением способность (инициализатор объекта) и возможность получения более точной информации о причине исключений при инициализации.

Есть и другие обходные пути. Например, мы можем проверить значения инициализатора перед его использованием в инициализаторе объекта:


Заключение

Это известная проблема. Это все еще существует и не исправлено. Мы можем обойти это, отказавшись от использования инициализаторов объектов и установщиков свойств (p.Name = (string) getName()). Также могут быть использованы другие обходные пути (например, проверка значений инициализатора перед использованием инициализатора объекта).

Мы должны осторожно использовать инициализаторы объекта. Если значения инициализатора могут вызвать исключение, мы должны рассмотреть инициализацию объекта с использованием установщиков свойств (p.Name = (string) getName()), потому что это упростит отладку в будущем.

...