我正在开发一个库,我希望在库中抛出的所有异常中包含其他上下文信息,
包括在非特定于库的例外中,例如System.NullReferenceException
。 (最有可能的是,这些附加信息将帮助开发人员调试其代码和/或数据的问题。)
编辑:我的库正在处理一些大型数据结构,这些结构也可以非常嵌套。因此,我希望将抛出异常时处理的字段的完整路径作为附加信息包括在内,例如, MyClass.Field1.SomeArray[10].SomeOtherField.ThisFails
。 (我认为在这种情况下,在异常中添加一条非常有用的信息)。
解决方案1.我知道包装是在异常中包含其他上下文信息的推荐方法。
但我认为它有一个很大的缺点:你必须改变例外的类型。
因此,例如,OutOfMemoryException
将包含在某些特定于库的异常中,并且应用程序处理此特定异常将更加困难
(开发人员必须检查InnerException
属性的类型)。
这就是我想避免包含异常的原因。 (对包装的另一个考虑是我的库将使用用户实现的一些对象,这可能抛出它们的特定异常,我不想修改这些异常的类型。)
解决方案2.我决定使用Exception.Data
字典在异常中包含此附加信息。
此解决方案的缺点是附加信息不会包含在Exception.ToString()
的默认实现的结果中。
(我想在Exception.ToString()
的结果中包含其他信息,以便开发人员可以很容易地看到这些信息,例如
log.Write(ex.ToString());
;通过这种方式,开发人员将不会学习如何从我的库中调用其他格式化方法。)
修改:
解决方案3.我想使用反射在exception.Message
字段的末尾附加我的信息(因为这是重读的)。我有什么理由可以避免这种情况吗?
这个问题有没有其他可能的解决方案可以避免解释的缺点?如果没有其他更好的解决方案,上面哪个是最常见的实现?
修改:
我想说清楚,因为这是一个库,我无法控制用户将使用哪种异常日志记录以及他们将阅读多少库文档。我希望通过在ex.ToString()
的结果中包含尽可能多的信息来避免无意义的支持请求。
此外,性能对于此库非常重要(可能仅适用于营销,但后来更为重要)因此,对于表现良好的方案,我希望避免任何额外的处理。也就是说,如果可能的话,我还想避免一些额外的检查,例如if (data.Field == null) throw new ArgumenNullException("someField");
,因为如果数据有问题,代码将稍后失败NullReferenceException
,我可以捕获一个方便的位置,并使用一些方法在其中添加数据。
答案 0 :(得分:2)
如果您愿意添加新方法(例如ToLogString()
)作为Exception
类型的扩展方法,则可以将默认ToString()
与来自{{1}的内容相结合}}
答案 1 :(得分:1)
我建议您定义一组广泛的案例,并在某种程度上更精细的详细例外,您知道您的库可以抛出。这些例外情况会阻止您的库达到其定义的期望(在您的控制范围内),即使由于用户部分的误用而引发异常。
例如,缺少Null值 - 因为你不能总是强制你的库的用户遵循你所规定的规则抛出自然的东西。 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
异常将使得修复代码变得更加困难。
现在有一些警告......例如数据库异常或业务类型代码中的异常会泄露您系统的敏感内部。在这些情况下,您可能被迫包装和/或检查特定的业务类异常或数据库异常,记录原始异常,并使用guid或对您记录的异常的一些引用返回SomthingBadHappened
异常。
另一个边缘案例是Web服务库,它可能依赖于特定的http响应代码。为此,我将研究异常屏蔽。
答案 2 :(得分:1)
一些想法:
事实上,在实践中很少需要捕获特定的异常,或者在异常中提供额外的信息。在附加信息仅用于记录的情况下尤其如此。
P.S。有关日志记录的一些有趣想法,请参阅Semantic Logging Application Block。即使你不使用,它的想法也很有趣。
答案 3 :(得分:0)
你的意思是这样吗?
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.Message中的某处插入e.GetType()。ToString(),如果您需要...但是因为属性只读消息您需要在 base 调用中传递您希望的格式代码
我对c#和异常都不熟悉所以...当我看到你的问题时,我开始在这里尝试一些事情,看起来你抛出异常时得到的控制台消息是消息属性...并且在构造函数中传递字符串时设置此属性,并且由于其只读而无法更改,因此我认为这可以解决您的问题