3

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

4 回答 4

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);

这取决于您的班级是否关心是否有空值。

如果您执行 null 检查的唯一原因是避免将 null 传递给TryAdd,那么请不要费心检查。 TryAdd会做它自己的检查并抛出异常。

如果在某些时候您认为您可能使用允许 null 的不同集合,但您仍然希望您的集合没有 null,那么您应该检查自己。如果将来发生这种变化,这将为您提供保护。

参数的验证应该始终是方法所做的第一件事。如果参数无效,那么做任何其他事情都没有意义。

如果你打算用它做某事,你应该只捕获一个异常。如果您只是要重新抛出,或者创建一个等效的新表达式,那么不要费心去捕捉它。

于 2013-04-17T16:36:10.597 回答
1

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

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

我最喜欢的可能是Item == null使用自定义错误消息进行检查,但那是因为我喜欢自定义错误消息。我发现它们对我更有用,但请确保在调用此方法的事物周围存在错误处理,以便错误不会导致更上游的未处理异常。

于 2013-04-17T16:43:45.687 回答
1

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

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

基本思想是,如果 an出于某种原因而不是null 而ArgumentNullException逃脱,则不应由可能期望您提供 an 的代码捕获此类异常。ConcurrentDictionary.AdditemArgumentNullException

于 2013-04-18T15:52:05.967 回答
0

在您的示例中,它们都太相似了,但我会选择第一个选项:

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

因为它不需要 acatch并且看起来更紧凑。如果需求发生变化并且不会添加更多代码行,它还可以让您扩展条件,例如

if (item==null || item.Name == null)
    throw...
于 2013-04-17T16:35:43.983 回答