2013-04-17 2 views
3

ConcurrentSet<T>을 구현하는 데 ConcurrentDictionary<TKey, TValue>을 사용합니다.래핑 된 객체의 예외를 다시 throw

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

ConcurrentDictionary<TKey, TValue>은 null 키가있는 쌍을 포함 할 수 없습니다.

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

을해야 또는한다 I,

try 
{ 
    return this.collection.TryAdd(item, 0); 
} 
catch (ArgumentNullException) 
{ 
    throw new ArgumentNullException("item", "Item must not be null."); 
} 

을 또는한다 I,

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

예외를 다시 던지면서 SO 응답을 확인하십시오. http://stackoverflow.com/questions/881473/why-catch-and-rethrow-exception-in-c –

+1

요점은 무엇인지에 대한 정보를 추가해주세요. ArgumentNullException'을'ArgumentNullException'으로 래핑 할 때? –

+1

@IlyaIvanov : 근본적인 문제는 'ConcurrentSet.Add'가 null 값으로 호출 되었기 때문에 스택 추적이 표시되어야한다는 것입니다. 게다가 throw 된 예외는 null 인수의 이름이 "key"가 아니라 "item"임을 나타냅니다. – supercat

답변

1

당신이해야 할 일은 클래스를 문서화하고자하는 것에 달려 있습니다. null 항목을 추가하려는 시도가 명시되지 않은 방식으로 실패 할 수 있음을 문서화하려면 간단히 직접 전화를 걸고 예외를 버블 링하십시오. 을 item과 동등한 ParamName과 함께 반환하고 null 키를 수신했을 때 ConcurrentDictionary의 동작에 의존하기를 원하지 않는다고 문서화하려면 ConcurrentDictionary으로 전달하기 전에 인수를 확인해야합니다. 당신은 당신의 코드가 item 동일한 ParamName와 함께 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 

item 이외의 이유로 ConcurrentDictionary.Add에서 ArgumentNullException 이스케이프가 null 인 경우, 이러한 예외가 하지ArgumentNullException을 기대할 수있는 코드에 의해 체포해야한다는 것을 기본 개념 너에게서.

+0

+1 또한 여러 인증 문제를 해결 했으므로 좋습니다. 고맙습니다. –

+0

@SafakGür : 매개 변수 유효성 검사의 실행 시간에 미치는 영향은 매우 작을 수 있습니다. 참조가 사용되기 전에 레지스터에로드되어야 할 것이므로 .NET 대신 코드 생성 도구가 필요할 것입니다. 참조를 사용하기 전에 널을 테스트 할 코드를 생성하는 것보다 런타임 환경을 설정하여 null 참조가 하드웨어 트랩을 트리거하고 'NullReferenceException'으로 변환되도록합니다. 프레임 워크가 수행한다는 사실은 null 테스트를 피하는 데 약간의 성능 이점이 있음을 암시합니다. – supercat

+0

와우, 세부 감사드립니다. 언급하기 전에 성능에 미치는 영향에 대해서는 절대로 생각하지 않았습니다. 내 동기 부여는 오히려 원시적 인 것이 었습니다. 뭔가를 검증하고 같은 유효성 검사를 수행하고 던지기와 거의 동일한 예외를 throw하는 메서드를 호출하는 것은 잘못되었습니다. 그러나 특성을 아는 것이 좋습니다. –

0

그들은 모두 너무 유사하지만 나는 첫 번째 옵션과 함께 갈 것 :

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

그것은 catch을 필요로하고 컴팩트 보이는하지 않는 한. 또한 요구 사항이 변경 될 경우 조건을 확장 할 수 있으며, 그와 같은

if (item==null || item.Name == null) 
    throw... 
+0

왜'TryAdd'가 잘못된 예라고 생각하십니까? –

+0

@ ŞafakGür 잘못된 예가 아니라, 나쁘다. TryXYZ는 대개 예외에 대해서는 조용하게 유지하지만이 경우에는 그렇지 않습니다. –

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 확인을 수행하는 유일한 이유는 null을 TryAdd으로 전달하지 않는 것이므로 검사를 거치지 않아도됩니다. TryAdd은 자체 검사를 수행하고 예외를 throw합니다.

null을 허용하는 다른 콜렉션을 사용할 수 있다고 생각하지만 콜렉션에 널 (null)이 없도록하려면, 자신을 점검해야합니다. 그 변화가 미래의 시점에서 일어날 경우, 이것은 당신을 보호 할 것입니다.

매개 변수의 유효성 검사는 항상 메소드가 수행하는 첫 번째 작업이어야합니다. 매개 변수가 유효하지 않으면 다른 작업을 수행 할 필요가 없습니다.

예외를 잡으려는 경우에만 예외를 catch해야합니다. 다시 던지거나 동등한 새로운 표현식을 만들려는 경우, 잡기가 쉽지 않습니다.

+0

두 번 upvote 할 수 있으면 좋겠어요! –

+0

+1 나에게서. 스택 추적이 오류 지점에 가까워지기 때문에 (즉, 구현의 종류 인 내부 컬렉션의 Add()가 아닌 외부 클래스의 Add()에 null을 전달한 코드) 세부 묘사). 또한 나는'Contract.Requires (item! = null) ''을 사용 하겠지만, 그것은 완전히 다른 문제이다. –

+0

"null 검사를 수행하는 유일한 이유는 TryAdd에 null을 전달하는 것이 아니라면 검사를 거치지 않아도됩니다. TryAdd는 자체 검사를 수행하고 예외를 throw합니다." - 정확히, 그러나 던져진'ArgumentNullException'은'의심의 주요 원인 인'ConcurrentDictionary '에 의해 던져지기 때문에'ParamName' 속성이 "key"로 설정됩니다. 나는 ConcurrentDictionary 를 래핑합니다. 그러나 구현의 세부 사항이며 그것을 드러내지 않는 것이 좋습니다. 소비자의 경우 null 값을 허용하지 않는 스레드 안전 해시 세트처럼 보일 것입니다. –

1

나는 당신이 무엇을 말하려합니까? 이 아니라 원하는 효과에 따라 달라집니다. 오류를 삼키고 사용자에게 표시하지 않겠습니까? catch 상자에 오류를 다시 throw하지 말고 try-catch를 포함하십시오. 맞춤 오류 메시지를 원하십니까, Item == null 수표로 가십시오. 새로운 예외 인스턴스를 생성하는 데별로 사용하지 않으므로 어쨌든 예외가 발생합니다.

나머지는 간다. 오류를 로깅하지 않거나 특별히 업스트림을 처리하지 않으면 오류를 다시 잡아서 다시 포착 할 필요가 없습니다. 그렇지 않으면, 이것은 개인적인 스타일과 당신이 커스텀 에러 메시지를 원하는지 아닌지에 달려 있습니다.

내 즐겨 찾기는 아마도 사용자 정의 오류 메시지로 확인하는 Item == null인데, 사용자 지정 오류 메시지가 좋기 때문입니다. 그 (것)들이 저를 위해 더 유용하다는 것을 찾아 내고, 그러나 이 방법이라고 주변에 실수 취급이다는 것을 확인하십시오, 그래야 더 이상 처리되지 않는 예외를 일으키는 원인이되지 않는다.

+0

+1 통찰력. 그래서, 커스텀 에러 메시지를 사용하고 랩핑 된'ConcurrentDictionary '의 존재를 숨기려면 (구현의 세부 사항입니다), 사전의 메소드를 호출하기 전에 제 자신의 에러 처리 *를 사용할 것을 권장합니다. 예외는 발생하지 않습니다. 그런 다음 try * catch *에서 호출하고 catch 블록에서 사용자 정의 예외를 throw합니다. 나의 딜레마의 원인은 사전이 이미 이러한 검증을 수행하고 있다는 사실이었습니다. 그래서 나는 그것을 허용해야한다고 생각했고, 예외를 던지면, 그 예외를 드러내는 대신에 커스텀 예외를 던지십시오. –

+0

사실, 당신이 정말 원하지 않는 한, try catch 내에서 사전 자체에 add를 호출 할 필요가 없습니다. 정말 나쁜 생각은 아닙니다. 중복 오류로 끝날 수도 있습니다. 결국 그것은 사전입니다. 그러나 내가 언급 한 것은 표시된 메서드 밖에 있었는데,이 메서드를 호출하면 일부 수준에서 try catch 블록 안에 있는지 확인합니다. 그렇지 않으면 방금 던진 사용자 지정 예외가 처리되지 않은 예외 오류를 상류로 가져옵니다. – Nevyn

관련 문제