Я работаю над библиотекой, и я хочу включить дополнительную контекстуальную информацию во все исключения, которые выбрасываются из библиотеки, в том числе в исключениях, которые не являются специфичными для библиотеки, например System.NullReferenceException
. (Скорее всего, эта дополнительная информация поможет разработчикам отладить проблемы с их кодом и/или данными.) EDIT: Моя библиотека обрабатывает некоторые большие структуры данных, которые также могут быть очень вложенными. Поэтому я хочу включить в качестве дополнительной информации полный путь поля, который обрабатывается при MyClass.Field1.SomeArray[10].SomeOtherField.ThisFails
исключения, например MyClass.Field1.SomeArray[10].SomeOtherField.ThisFails
. (Я думаю, что в этом случае это будет очень полезная информация для добавления исключений).
Решение 1. Я знаю, что упаковка является рекомендуемым способом включения дополнительной контекстной информации в исключения. Но я думаю, что это имеет большой недостаток: вы должны изменить тип исключений. Таким образом, например, OutOfMemoryException
будет обернуто в конкретное исключение библиотеки, и приложение будет более сложно обрабатывать это конкретное исключение (разработчикам придется проверять тип свойства InnerException
). Поэтому я хочу избежать исключения исключений. (Дополнительным соображением против упаковки является то, что моя библиотека будет работать с некоторыми объектами, реализованными пользователями, что может вызывать определенные исключения, и я не хочу изменять тип этих исключений.)
Решение 2. Я решил использовать словарь Exception.Data
чтобы включить эту дополнительную информацию в исключения. Недостатком этого решения является то, что дополнительная информация не будет включена в результат реализации Exception.ToString()
по умолчанию. (Я хотел бы включить дополнительную информацию в результат Exception.ToString()
, чтобы она была легко видна разработчикам, которые регистрируют исключения с чем-то вроде log.Write(ex.ToString());
разработчики не узнают, как вызывать другой метод форматирования из моей библиотеки.)
EDIT: Решение 3. Я думал о добавлении моей информации в конце exception.Message
Поле сообщения с использованием рефлексии (так как это readolnly). Есть ли веские причины, почему я должен избегать этого?
Есть ли другие возможные решения этой проблемы, которые позволят избежать объясненных недостатков? Если нет других лучших решений, какая из них является наиболее распространенной?
EDIT: хочу пояснить, так как это библиотека, у меня нет большого контроля над тем, какую регистрацию исключений будет использовать пользователь, и какую часть документации библиотеки они будут читать. Я хочу избежать бессмысленных запросов поддержки, включив как можно больше информации в результат ex.ToString()
.
Кроме того, производительность важна для этой библиотеки (это может быть только для маркетинга, но тогда это еще более важно), поэтому для сценариев с хорошим поведением я хочу избежать дополнительной обработки. То есть, если это возможно, я также хочу избежать некоторых дополнительных проверок, например if (data.Field == null) throw new ArgumenNullException("someField");
поскольку, если что-то не так с данными, код будет просто немного позже с NullReferenceException
, который я могу поймать в удобном месте и использовать некоторый метод для добавления в него данных.
Если вы открыты для добавления нового метода, такого как ToLogString()
, в качестве метода расширения для типа Exception
, который мог бы объединить по умолчанию ToString()
с материалами из Exception.Data
.
Я бы порекомендовал вам определить набор широкого корпуса и, в некоторой степени, более тонкие подробные исключения, которые, как вы знаете, могут бросить вашу библиотеку. Это будут исключения, которые запрещают вашей библиотеке соответствовать определенным ожиданиям (в пределах вашего контроля), даже если исключение выбрано из-за неправильного использования в части пользователей.
Например, отсутствие нулевых значений. Поскольку вы не всегда можете принуждать пользователей вашей библиотеки следовать правилам, которые вы выкладываете, бросайте то, что естественно. throw new ArgumentNullException("Some message")
или выбросить new MyLibraryArgumentNullException()
.
В некоторых случаях вы можете ввести набор пользовательских исключений, таких как throw new MyLibraryCantDoThisBecauseOfThatException().
... Ты понимаешь мой смысл.
Если вы добавите все необходимые проверки, чтобы убедиться, что вы можете создавать выходные данные, не делайте ничего лишнего.
public SomeObject SomeProcess(AnotherObject anotherObject){
if(anotherObject==null) throw ArgumentNullException();
if(anotherObject.MustBePositive<=0) throw ArgumentException("x must be positive");
if(_someDepenedencyMyLibaryCreates.NotThere) throw new MyLibraryMissingDepencyException()
NowIHaveEnoughInfoToDoMyJobWithNoTry();
}
Следуя этим рекомендациям по пути, который ваша библиотека должна проверить и выбросить все исключения, которые не позволили бы ему выполнить свою задачу в вашей саранче контроля. Если у вас возникнут проблемы с памятью и т.д., Я бы позволил этим людям естественным образом вернуться из вашей библиотеки, потому что вы выполнили свою сделку. Пользователь должен получить достаточную информацию из вашего исключения, чтобы его можно было легко отследить без специального кольца декодера.
Это оставляет только ошибки в вашем коде, такие как индекс за пределами диапазона. Я бы позволил им естественным образом вытекать из-за того, что обертывание всего и бросание исключения SomethingBadHappened
затруднит исправление кода.
Теперь есть оговорки к этому... такие как исключения или исключения базы данных в коде бизнес-типа, которые могут выявить уязвимые внутренние компоненты вашей системы. В таких ситуациях вам может потребоваться обернуть или проверить конкретное исключение исключительной ситуации в бизнес-классе или базу данных, регистрацию оригинала и возврат исключения SomthingBadHappened
с помощью указателя или некоторую ссылку на исключение, которое вы зарегистрировали.
Еще один краевой случай - это библиотеки веб-сервисов, которые могут полагаться на определенный код ответа HTTP. Для этого я бы посмотрел на Exception Shielding.
Некоторые мысли:
Дело в том, что на практике очень мало нужно улавливать определенные исключения, или предоставлять дополнительную информацию в исключении. Это особенно верно в случае, когда дополнительная информация будет использоваться только для ведения журнала.
PS О некоторых интересных идеях по регистрации вы найдете в блоке приложений семантического журнала. Даже если вы не используете, идеи интересны.
Вы имеете в виду что-то вроде этого?
namespace YourLibrary
{
class Example
{
static void Main()
{
int [] v = null;
try
{
v[1] = 10;
}
catch(Exception e)
{
throw new MyException(e);
}
}
}
public class MyException : Exception
{
public MyException(Exception e):base(e.GetType().ToString() + e.Message)
{
}
}
}
Поступая таким образом, вы включаете информацию из предыдущего исключения в свой новый... Вы можете вставить e.GetType(). ToString() где-то внутри e.Message тоже, если вы хотите... но так как свойство Message is readonly вам необходимо передать код для форматирования в этом базовом вызове
Я новичок и для С#, и для исключений, поэтому... Когда я увидел ваш вопрос, я начал пробовать кое-что здесь, и кажется, что сообщение консоли, которое вы получаете, когда генерируется исключение, - это то, что находится внутри свойства Message... и это свойство задается при передаче строки в конструкторе и не может быть изменено с момента его чтения, поэтому я думаю, что это решит вашу проблему