我使用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;
}
这样做的正确方法是什么?
答案 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...