重新抛出包装对象的异常

时间:2013-04-17 16:25:56

标签: c# .net exception exception-handling try-catch

我使用ConcurrentDictionary<TKey, TValue>来实现ConcurrentSet<T>

public class ConcurrentSet<T> : ISet<T>
{
    private readonly ConcurrentDictionary<T, byte> collection;
}

ConcurrentDictionary<TKey, TValue>不能包含空键对。

// 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;
}

这样做的正确方法是什么?

4 个答案:

答案 0 :(得分:7)

我会选择这个

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的其他集合,但您仍希望您的集合不具有空值,那么您应该检查自己。如果在未来某个时间点发生变化,这将保护您。

参数的验证应该始终是方法的第一步。如果参数无效,则无需做任何其他事情。

如果要对它进行某些操作,您应该只捕获异常。如果您只是要重新抛出,或者创建一个等效的新表达式,那么就不要去抓它了。

答案 1 :(得分:1)

我会说,你应该做什么取决于你想要的效果。您想吞下错误而不是向用户显示错误吗?不要在catch框中重新抛出错误,但请包含try-catch。您是否需要自定义错误消息,请使用Item == null检查。生成一个新的异常实例并没有多大用处,所以无论如何都是如此。

就其余部分而言..如果你没有记录错误,或者特意进一步向上游处理它,那么在捕获它之后就没有必要重新抛出错误了。否则,这取决于个人风格以及是否需要自定义错误消息。

我最喜欢的可能是Item == null检查自定义错误消息,但这是因为我喜欢自定义错误消息。我觉得它们对我来说更有用,但要确保在调用这个方法的事情周围有错误处理,这样错误就不会导致上游的未处理异常。

答案 2 :(得分:1)

你应该做什么取决于你想要记录你班级的内容。如果您希望记录添加空项目的尝试可能以未指定的方式失败,那么只需直接进行调用并让任何异常冒泡。如果您希望记录,您将返回ArgumentNullException ParamName等于item,并且不希望在收到空键时依赖ConcurrentDictionary的行为,然后你应该在将参数传递给ConcurrentDictionary之前检查一下。如果您希望记录您的代码将ArgumentNullException ParamName等于item,但愿意依赖ConcurrentDictionary验证其参数并抛出{ {1}},如果性能至关重要,另一种可能性是:

ArgumentException

这个代码避免了在参数非空(在99.9999%的情况下应该是这种情况)的情况下参数验证的任何额外成本,但仍然会确保它只声称是{{{ 1}}在出现预期原因的情况下发生此类异常的情况;如果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参数传递给它内部调用的方法,即使给它一个非空项添加,上面的代码也会确保原始异常堆栈跟踪不会丢失。请注意,另一种可能性可能是:

ArgumentNullException

基本的想法是,如果ConcurrentDictionary 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的代码。

答案 3 :(得分:0)

在你的例子中,它们非常相似,但我会选择第一个选项:

if (item == null)
    throw new ArgumentNullException("item", "Item must not be null.");

因为它不需要catch并且看起来更紧凑。如果需求发生变化,并且不会添加更多代码行,例如

,它还可以让您扩展条件
if (item==null || item.Name == null)
    throw...