#c# #.net #exception #argumentexception
Вопрос:
При написании классов для внутренней обработки в .Net я часто использую ArgumentException
, чтобы указать, что с данными что-то не так и их нельзя обработать. Из-за характера программы текст, который я помещаю в эти исключения, иногда имеет отношение к пользователю, и поэтому он часто отображается в пользовательском интерфейсе.
Однако я заметил, что ArgumentException
это специально переопределяет Message
свойство, чтобы добавить свою собственную строку, чтобы указать, какой аргумент вызвал исключение. Я не хочу, чтобы этот дополнительный текст загрязнял сообщение, поскольку фактическое имя аргумента-это информация о внутренней обработке, которую действительно не нужно показывать пользователю, и тот факт, что он добавляет разрыв строки и локализуется, портит форматирование сообщения, которое я показываю в пользовательском интерфейсе. Единственный способ обойти это-не указывать исключению фактическое имя аргумента, но я также не хочу саботировать свою собственную отладку / ведение журнала, удаляя эту информацию.
Конечно, я мог бы использовать свой собственный класс исключений, но поскольку многие из этих методов предназначены для сжатия и распаковки проприетарных форматов файлов в старых играх DOS, и я хочу, чтобы эти методы были задокументированы в вики и, как правило, были легко доступны для использования кем-либо другим, я бы предпочел сохранить их портативными и избегать зависимости от других внешних классов. И, в качестве примечания, подклассы ArgumentException
, конечно, вызовут ту же проблему.
Первоисточник:
public override String Message
{
get {
String s = base.Message;
if (!String.IsNullOrEmpty(m_paramName)) {
String resourceString = Environment.GetResourceString("Arg_ParamName_Name", m_paramName);
return s Environment.NewLine resourceString;
}
else
return s;
}
}
(из referencesource.microsoft.com)
Поскольку это фактически переопределяет Message
свойство, кажется, что нет нормального способа добраться до реального сообщения, которое хранится внутри. Разделение на разрыв строки кажется беспорядочным и потенциально ненадежным в зависимости от различий в локализации (и в сообщении, которое я даю, уже могут быть разрывы строк), и использование отражения для этого кажется довольно беспорядочным. Есть ли чистый способ восстановить исходное сообщение?
(Публикую это здесь с решением для документирования причин, так как это поведение действительно расстроило меня, когда я столкнулся с ним)
Комментарии:
1. Я не понимаю вашей точки зрения о «зависимости от внешних классов». Либо используйте
ArgumentException
для чего-то, что является аргументом, либо свой собственный класс исключений. В чем проблемаcatch(MyOwnException)
?2. Эта проблема заключается в том, что я хотел, чтобы код был портативным и простым в использовании для других людей. И я понял эту проблему только после
ArgumentException
интенсивного использования.3. Но пользователям все равно нужен ваш собственный код, так почему бы также не отправить этот дополнительный класс? В любом случае: вы также можете использовать
InvalidOperationException
.4. В этом нет никакого смысла. Прежде всего, вы полагаетесь на исключения для не исключительных случаев, что рассматривается как анти-шаблон в зависимости от того, кого вы спрашиваете. Во-вторых, нет никакого смысла в том, что вы «не хотите использовать свои собственные исключения». Просто создайте классы исключений, которые правильно идентифицируют то, что произошло, с помощью простых имен, и все.
5. Как «файл содержит неверные данные для обработки/распаковки этой функцией» не является исключением? Это сообщение было в основном сделано из-за разочарования тем, почему стандартный класс исключений .net исказит сообщение об исключении, помещенное в него программистом.
Ответ №1:
Поскольку я не хотел углубляться в размышления, я решил, что хорошим способом получить исходные данные без связанного поведения класса будет их сериализация. Имена свойств в сериализованной информации очень просты, и к ним можно получить доступ без ArgumentException
того, чтобы получатель искажал их своими собственными дополнениями.
Код для этого оказался довольно простым:
public static String RecoverArgExceptionMessage(ArgumentException argex)
{
if (argex == null)
return null;
SerializationInfo info = new SerializationInfo(typeof(ArgumentException), new FormatterConverter());
argex.GetObjectData(info, new StreamingContext(StreamingContextStates.Clone));
return info.GetString("Message");
}