#c# #.net #exception #exception-handling #customization
#c# #.net #исключение #настройка
Вопрос:
Мне было интересно, существует ли разумный способ настройки сообщений об исключениях, которые генерируются .NET framework? Ниже приведен фрагмент кода, который я часто пишу во многих различных сценариях для достижения эффекта предоставления разумных сообщений об исключениях моим пользователям.
public string GetMetadata(string metaDataKey)
{
// As you can see, I am doing what the dictionary itself will normally do, but my exception message has some context, and is therefore more descriptive of the actual problem.
if (!_Metadata.ContainsKey(metaDataKey))
{
throw new KeyNotFoundException(string.Format("There is no metadata that contains the key '{0}'!", metaDataKey));
}
// This will throw a 'KeyNotFoundException' in normal cases, which I want, but the message "The key is not present in the dictionary" is not very informative. This is the exception who's message I wish to alter.
string val = _Metadata[metaDataKey].TrimEnd();
return val;
}
Как вы можете видеть, я, по сути, создаю дублированный код только для того, чтобы использовать другое (лучшее) сообщение.
Редактировать:
По сути, я ищу что-то вроде этого:
KeyNotFoundException.SetMessage("this is my custom message!")
{
// OK, now this will send off the message I want when the exception appears!
// Now I can avoid all of that silly boilerplate!
string val = _Metadata[metaDataKey].TrimEnd();
}
В любом случае, я не думаю, что такая функция существует, но если бы она существовала, я был бы действительно очень доволен. Кто-нибудь сталкивался с проблемой такого типа раньше? Похоже, что в конце концов мне понадобится какой-то метод расширения…
Ответ №1:
Если я чего-то не упустил в вашем вопросе, это именно то, что вы должны делать. Я почти уверен, что каждое исключение включает перегрузку, которая принимает string message
в качестве параметра. Если вы хотите предоставить информацию сверх «значения по умолчанию», предоставляемого .NET, вам необходимо задать конкретное сообщение.
Комментарии:
1. Я думаю, все пропустили вопрос. Я знаю, что при попытке получить доступ к словарю появится ‘KeyNotFound’. Я просто хочу изменить сообщение, включенное в ЭТО ИСКЛЮЧЕНИЕ. Я не нахожу очень полезным выполнять проверку самостоятельно или перехватывать исходное исключение.
Ответ №2:
Кажется, вы делаете это правильно с самого начала. Однако я бы изменил способ проверки на наличие исключений:
public string GetMetadata(string metaDataKey)
{
try
{
string val = _Metadata[metaDataKey].TrimEnd();
return val;
}
catch (KeyNotFoundException ex)
{
// or your own custom MetaDataNotFoundException or some such, ie:
// throw new MetaDataNotFoundException(metaDatakey);
throw new KeyNotFoundException(string.Format("There is no metadata that contains the key '{0}'!", metaDataKey));
}
}
Комментарии:
1. Таким образом, вы теряете часть дорожки стека. Я бы добавил исходное исключение как InnerException к тому, которое вы создаете.
2. @whatknott Абсолютно. Я писал кратко, но вы правы.
3. почему это лучшая альтернатива моему первоначальному подходу? Или это просто другой синтаксис, потому что?
4. В основном это вопрос стиля и субъективно, но я считаю, что try / catch помогает разделить логику, а обработка ошибок — приятный и управляемый способ. Единственная причина, по которой я бы изменил способ, которым вы уже генерируете исключения, заключается в том, что вы хотите локализовать текст, поскольку тогда вы могли бы заставить свой пользовательский обработчик исключений выбрать подходящую текстовую строку для использования.
Ответ №3:
Просто унаследуйте от KeyNotFoundException
класса и переопределите свойство Message, чтобы сгенерировать более значимое сообщение, а затем используйте свой собственный класс исключений с соответствующим конструктором. Это именно то, для чего предназначалось наследование, добавляющее ценность. т.Е.
throw new MetaDataKeyNotFoundException(string metaDataKey);
Комментарии:
1. Но посмотрите на его вопрос. Наследование не может помочь ему с тем, чего он хочет, на самом деле. Он уже «переопределяет» сообщение, создавая собственное исключение с собственным текстом сообщения.
2. Зачем столько хлопот, когда он может просто предоставить правильное сообщение в конструкторе исключения, которое он генерирует? Любой* код, который может вызвать исключение, должен быть в
try/catch
любом случае завернут (*- для заданного значения «any»).3. Ну, это могло бы быть как
throw new MetaDataKeyNotFoundException(string metaDataKey);
, что было бы более кратким.4. Мне не нравится вводить один и тот же текст несколько раз… идентификатор наследуется от него и предоставляет пользовательский конструктор, чтобы он заполнял сообщение тем, что вы хотите. Таким образом, вы также можете переместить строку в файл ресурсов. Крис Уолш также высказывает хорошую мысль, imo. У людей ОЧЕНЬ сильные чувства по поводу того, правильно ли наследовать исключения и предоставлять пользовательские исключения.
Ответ №4:
Exception
Класс уже поддерживает добавление пользовательских данных пользователя, связанных с конкретным событием или сценарием, в котором произошла ошибка, с использованием свойства Exception.Data
.
Из записи MSDN для этого свойства, акцент мой:
Получает коллекцию пар ключ / значение, которые предоставляют дополнительную пользовательскую информацию об исключении.
Я знаю, что вы хотели переопределить Message
свойство, но с помощью Data
вы можете добиться того же, просто убедившись, что обработчик исключений знает, как обращаться с этими дополнительными данными.
Комментарии:
1. да, но я пытаюсь избежать дополнительных блоков try / catch. Однако это довольно хороший подход для других сценариев…
Ответ №5:
Исключение — это объект. Как и в случае с большинством объектов, вы не можете контролировать, как создатель создает объект, независимо от того, является ли создатель .NET Framework.
Как бы вы вообще сообщили .NET Framework, какое сообщение создавать при каких обстоятельствах? Вы хотели бы получить одно сообщение на KeyNotFoundException
в случае, который вы опубликовали, и другое сообщение в других обстоятельствах. Как бы вы различили эти две ситуации?
Ответ №6:
KeyNotFoundException.setMessage («это мое пользовательское сообщение!»);
Такой функции нет (возможно, кроме вмешательства во внутренние компоненты или ресурсы). Но как это должно работать в любом случае. Вы бы изменили сообщение для каждого фрагмента кода, который использует исключение — для некоторых из которых ваше новое сообщение вообще не имело бы смысла.
Рассмотрите возможность произвольного использования Dictionary
класса или даже какого-то совершенно другого кода, который следует «наилучшей практике» повторного использования существующих типов исключений, все они внезапно будут использовать ваше (очень часто) пользовательское сообщение об ошибке.
Комментарии:
1. да, как я уже сказал, я не думал, что такая функция существует. Конечно, если бы это произошло, я бы надеялся, что никто не был бы настолько глуп, чтобы реализовать это для обеспечения поведения, которое вы описываете.
Ответ №7:
Вот решение, которое я придумал, но я хотел бы отметить, что это скорее исправление, чем что-либо еще. Это действительно работает, но, вероятно, подходит не для всех приложений. Я даже не мог придумать для этого подходящего названия.
public class ContextDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
public TValue this[TKey key, string context]
{
get
{
if (!this.ContainsKey(key))
{
throw new KeyNotFoundException(string.Format("There is no {0} that contains the key '{1}'!", context, key));
}
return this[key];
}
set { this[key] = value; }
}
}
Итак, теперь я могу сказать что-то вроде этого и получить более описательное сообщение об исключении, которое я действительно хочу.
var _MetaData = new ContextDictionary<string,string>();
string val = _Metadata[metaDataKey, "metadata"].TrimEnd();