重新抛出包装对象的异常
本文关键字:异常 对象 包装 新抛出 | 更新日期: 2023-09-27 18:09:36
我使用ConcurrentDictionary<TKey, TValue>
来实现ConcurrentSet<T>
。
public class ConcurrentSet<T> : ISet<T>
{
private readonly ConcurrentDictionary<T, byte> collection;
}
ConcurrentDictionary<TKey, TValue>
不能包含null键的pair
// summary, param, returns...
/// <exception cref="ArgumentNullException">
/// <paramref name="item" /> is null.
/// </exception>
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);
或者,我应该
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;
}
正确的方法是什么?
我会选择这个
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);
这取决于你的类是否关心是否存在null。
如果执行null检查的唯一原因是避免将null传递给TryAdd
,那么就不要检查了。TryAdd
会自己检查并抛出异常。
如果在某些时候你认为你可能会使用一个允许null的不同集合,但你仍然希望你的集合不包含null,那么你应该自己检查一下。这将保护您在将来的某个时间点发生更改。
参数验证应该总是方法要做的第一件事。如果参数无效,那么做其他任何事情都没有意义。
只有当你要处理异常时,你才应该捕捉异常。如果你只是想重新抛出,或者创建一个等价的新表达式,那么就不要捕获它了。
你应该做什么取决于你想记录你的类做什么。如果您希望记录添加空项的尝试可能以未指定的方式失败,那么只需直接进行调用,并让任何异常冒出。如果您希望记录您将返回ArgumentNullException
,其中ParamName
等于item
,并且不希望依赖于ConcurrentDictionary
在接收空键时的行为,那么您应该在将其传递给ConcurrentDictionary
之前检查参数。如果您希望记录您的代码将抛出ArgumentNullException
,其中ParamName
等于item
,但愿意依赖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;
}
这段代码避免了在参数不为空的情况下(99.9999%的情况应该是这样)参数验证的任何额外成本,但仍然确保它只会在由于预期原因发生这种异常的情况下声称是ArgumentNullException
的来源;如果ConcurrentDictionary
中的错误导致它意外地将一个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
由于item
为null以外的原因从ConcurrentDictionary.Add
中转义,那么这种异常应该不被可能期望从您那里获得ArgumentNullException
的代码捕获。
我想说的是,你应该做什么取决于你想要的效果。您想吞下错误而不向用户显示它吗?不要在catch框中重新抛出错误,但要包含try-catch。如果您想要自定义错误消息,请使用Item == null
检查。生成一个新的异常实例并没有多大用处,所以不管怎样,这都是不可能的。
我最喜欢的可能是Item == null
检查自定义错误消息,但那是因为我喜欢自定义错误消息。我发现它们对我更有用,但要确保在调用这个方法的东西周围有错误处理,这样错误就不会导致未处理的异常。
在你的例子中,它们都太相似了,但我还是选择第一个选项:
if (item == null)
throw new ArgumentNullException("item", "Item must not be null.");
,因为它不需要catch
,看起来更紧凑。如果需求发生变化,它还可以让您扩展条件,而不会添加更多的代码行,例如
if (item==null || item.Name == null)
throw...