Получение исходного сообщения об ошибке из исключения ArgumentException

#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");
}