C# 重新引发已包装对象的异常
我使用C# 重新引发已包装对象的异常,c#,.net,exception,exception-handling,try-catch,C#,.net,Exception,Exception Handling,Try Catch,我使用ConcurrentDictionary实现ConcurrentSet 或者我应该 if (item == null) throw new ArgumentNullException("item", "Item must not be null."); return this.collection.TryAdd(item, 0); try { return this.collection.TryAdd(item, 0); } catch (ArgumentNullExc
ConcurrentDictionary
实现ConcurrentSet
或者我应该
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
return this.collection.TryAdd(item, 0);
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
throw new ArgumentNullException("item", "Item must not be null.");
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException x)
{
// I know, but I don't want to preserve the stack trace
// back to the underlying dictionary, anyway.
throw x;
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
// The thrown exception will have "key", instead of
// "item" as the parameter's name, in this instance.
throw;
}
或者我应该
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
return this.collection.TryAdd(item, 0);
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
throw new ArgumentNullException("item", "Item must not be null.");
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException x)
{
// I know, but I don't want to preserve the stack trace
// back to the underlying dictionary, anyway.
throw x;
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
// The thrown exception will have "key", instead of
// "item" as the parameter's name, in this instance.
throw;
}
或者我应该
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
return this.collection.TryAdd(item, 0);
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
throw new ArgumentNullException("item", "Item must not be null.");
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException x)
{
// I know, but I don't want to preserve the stack trace
// back to the underlying dictionary, anyway.
throw x;
}
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException)
{
// The thrown exception will have "key", instead of
// "item" as the parameter's name, in this instance.
throw;
}
正确的方法是什么?在您的示例中,它们非常相似,但我会选择第一个选项:
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
因为它不需要捕捉
,而且看起来更紧凑。如果需求发生变化,并且不会增加更多的代码行,它还可以让您在条件上进行扩展,例如
if (item==null || item.Name == null)
throw...
这两个我都同意
public bool Add(T item)
{
// This throws an argument null exception if item is null.
return this.collection.TryAdd(item, 0);
}
还是这个
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
return this.collection.TryAdd(item, 0);
这取决于类是否关心是否存在空值
如果执行空值检查的唯一原因是为了避免将空值传递给
TryAdd
,那么不要费心检查TryAdd
将执行自己的检查并引发异常
如果在某个时候您认为您可能会使用另一个允许null的集合,但您仍然希望您的集合不具有null,那么您应该检查自己。这将在将来某个时间点发生更改时保护您
参数验证应该始终是方法所做的第一件事。如果参数无效,那么做任何其他事情都没有意义
只有当您打算对异常进行处理时,才应该捕获它。如果您只是想重新抛出,或者创建一个新的等价表达式,那么就不要费心去捕捉它。我想说的是,您应该做什么取决于您想要的效果。是否要接受错误而不向用户显示?不要在catch框中重新抛出错误,但一定要包含try catch。要自定义错误消息,请执行项==null
检查。生成一个新的异常实例并没有多大用处,所以无论如何这都是不可能的
至于它的其余部分……如果您没有记录错误,或者没有专门处理更上游的错误,那么在捕获错误后就没有必要重新抛出错误。否则,这取决于个人风格以及是否需要自定义错误消息
我最喜欢的可能是使用自定义错误消息检查
Item==null
,但这是因为我喜欢自定义错误消息。我发现它们对我更有用,但要确保调用此方法的对象周围有错误处理,这样错误就不会导致更上游的未处理异常。您应该做什么取决于您想要记录类的操作。如果您希望记录添加空项的尝试可能会以未指定的方式失败,那么只需直接进行调用,并允许出现任何异常。如果您希望记录您将返回参数名
等于项
的ArgumentNullException
,并且不希望依赖ConcurrentDictionary
在接收空键时的行为,那么您应该在将参数传递到ConcurrentDictionary
之前检查参数。如果您希望记录代码将抛出参数名等于项的ArgumentNullException
,但愿意依赖ConcurrentDictionary
验证其参数并抛出ArgumentException
,并且如果性能至关重要,另一种可能性是:
try
{
return this.collection.TryAdd(item, 0);
}
catch (ArgumentNullException ex)
{
if (ex.ParamName == "key" && item == null)
throw new ArgumentNullException("item", "Item must not be null.");
else
throw;
}
该代码避免了在参数不为null的情况下(在99.9999%的情况下)进行参数验证的任何额外成本,但仍将确保在因预期原因发生异常的情况下,该代码仅声明为ArgumentNullException
的源;如果ConcurrentDictionary
中的错误导致它意外地将null参数传递给它内部调用的方法,即使给它添加了非null项,上述代码将确保原始异常堆栈跟踪不会丢失。请注意,另一种可能性可能是:
if (ex.ParamName == "key" && item == null)
throw new ArgumentNullException("item", "Item must not be null.");
else
throw new UnexpectedException(ex); // Probably a custom type
其基本思想是,如果一个ArgumentNullException
从ConcurrentDictionary.Add
中转义,而不是因为项
为空,这样一个异常不应该被那些可能期望您发出ArgumentNullException
的代码捕获。请查看关于重新抛出异常的答案:您能补充一下关于用ArgumentNullException
包装ArgumentNullException
的意义吗?@Ilyavanov:我认为这个想法是这样的由于基本问题是使用空值调用了ConcurrentSet.Add
,这就是堆栈跟踪应该显示的内容。此外,抛出的异常应该表明null参数的名称是“item”而不是“key”。@IlyaIvanov:就像supercat所说的,由ConcurrentDictionary
抛出的ArgumentNullException
将其ParamName
属性设置为“key”,而不是“item”。这不是此类的预期行为,它可能会混淆ConcurrentSet
的使用者,由于它通过异常的ParamName
属性和源自ConcurrentDictionary
@wonko79的堆栈跟踪公开了实现细节:这不是您提到的问题的重复,因为实际上这更多的是关于API设计而不是错误处理。ConcurrentSet的使用者应该处理ArgumentNullException,如果他们将null值传递给Add(T)方法,但是他们应该知道它是由并发字典抛出的,还是堆栈跟踪应该引导他们添加(T)而不是TryAdd(TKey,TValue)?哪一个更糟糕,验证一个将由包装类验证的参数,还是让第二个异常被初始化?+1来自我。我自己会选择这两种方法中的第二种——因为堆栈跟踪将在更接近错误点的地方开始