ReSharper / C #에서 "대리자 빼기 결과를 예측할 수 없습니다"?


124

사용하는 경우 myDelegate -= eventHandlerReSharper에서 (버전 6) 문제 :

대리자 빼기 결과는 예측할 수 없습니다.

이것의 합리적 근거는 여기 JetBrains에 의해 설명됩니다 . 설명은 의미가 있으며, 읽은 후 -대리인 에 대한 모든 사용을 의심합니다 .

어떻게 ,

  • ReSharper를 심술 궂게 만들지 않고 비 자동 이벤트를 작성할 수 있습니까?
  • 또는이를 구현하는 더 나은 및 / 또는 "올바른"방법이 있습니까?
  • 또는 ReSharper를 무시해도됩니까?

다음은 간단한 코드입니다.

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

Mono는 동일한 경고를 제공합니다. 다음은 문제에 대한 R #의 설명입니다. confluence.jetbrains.com/display/ReSharper/… (대리자 목록에만 적용됨)
thoredge

답변:


134

두려워하지 마세요! ReSharper 경고의 첫 번째 부분은 대리인 목록 제거에만 적용됩니다. 코드에서 항상 단일 대리자를 제거합니다. 두 번째 부분에서는 중복 델리게이트가 제거 된 후 델리게이트 순서에 대해 설명합니다. 이벤트는 구독자의 실행 순서를 보장하지 않으므로 실제로 사용자에게 영향을 미치지 않습니다.

위의 메커니즘은 예측할 수없는 결과를 초래할 수 있으므로 ReSharper는 대리자 빼기 연산자를 만날 때마다 경고를 발행합니다.

ReSharper는 멀티 캐스트 대리자 감산에 문제가있을 수 있기 때문에이 경고를 발행하지만 해당 언어 기능을 완전히 비난하지는 않습니다. 운 좋게도 이러한 문제는 특수한 경우에 있으며 단순한 이벤트를 계측하는 경우에는 이러한 문제가 발생할 가능성이 거의 없습니다. 자신의 add/ remove핸들러 를 구현하는 더 좋은 방법은 없습니다 .주의해야합니다.

일반적으로 유용한 경고에 민감하지 않도록 해당 메시지에 대한 ReSharper의 경고 수준을 "힌트"로 다운 그레이드하는 것이 좋습니다.


66
결과를 "예측 불가능"이라고 부르는 것은 R #의 나쁘다고 생각합니다. 그들은 매우 명확하게 지정되어 있습니다. "사용자가 예측할 수있는 내용이 아님"은 "예측할 수 없음"과는 전혀 다릅니다. (. 그것은 .NET 프레임 워크를 정의 과부하라고하는 것이 정확하지 -는 C # 컴파일러에 구운 것은 Delegate않습니다 하지 과부하 +-.)
존 소총

6
@Jon : 동의합니다. 나는 모두가 마이크로 소프트가 스스로 설정 한 높은 기준에 익숙해 졌다고 생각한다. .NET 세계의 많은 것들이 당신을 "성공의 구덩이에 빠지게"만드는 수준의 광택이있는만큼 높은데, 그 구덩이 옆에서 순조롭게 걸어가는 언어 기능을 만나게됩니다. 당신이 놓칠 수있는 기회는 일부 사람들이 어리석은 것으로 간주하고 다음과 같은 표시를 보증합니다 PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek 2012-06-24

5
@AllonGuralnek : 반면에, 이로 인해 실제로 문제가 발생한 사람에 대해 마지막으로 들었던 때는 언제입니까?
Jon Skeet

5
@Jon : 문제가 있다고 들었나요? 난하지 않았다 알고 이 질문에 게시되기 전에이 문제에 대한.
Allon Guralnek 2012-06-24

2
R #이 델리게이트 빼기에 대해 경고하지만 똑같은 문제있는 이벤트의 일반적인 구현에 대해서는 경고하지 않는 것이 궁금합니다 . 핵심 문제는 .net이 Delegate.Combine멀티 캐스트 델리게이트를 "평탄화" 하는 단일 을 사용 하므로 델리게이트 [X, Y] 및 Z가 주어지면 결과가 [X, Y, Z]인지 [[인지 알 수 없습니다. X는, Y는, Z는 (후자 대리자 유지 [X,Y]그 대리자로서 Target, 그 대리인의 Invoke방법 그와 같은 Method).
supercat

28

합계 또는 빼기 위해 대리자를 직접 사용해서는 안됩니다. 대신 당신의 분야

MyHandler _myEvent;

대신 이벤트로 선언되어야합니다. 이렇게하면 솔루션을 위험에 빠뜨리지 않고 문제가 해결되고 이벤트 사용의 이점이 있습니다.

event MyHandler _myEvent;

델리게이트 합계 또는 빼기 사용은 단순히 델리게이트를 할당 할 때 이벤트를 잃을 수 있기 때문에 위험합니다 (선언에 따라 개발자는 이것이 이벤트로 선언 될 때 멀티 캐스트 델리게이트임을 직접 추론하지 않습니다). 예를 들어,이 질문에 언급 된 속성이 이벤트로 플래그가 지정되지 않은 경우 아래 코드는 누군가가 단순히 대리인에게 할당 되었기 때문에 (유효합니다!) 두 개의 첫 번째 할당이 LOST로 간주됩니다.

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

Method3을 할당 할 때 두 개의 초기 구독을 완전히 잃었습니다. 이벤트 사용은이 문제를 방지하고 동시에 ReSharper 경고를 제거합니다.


이런 식으로 할 생각은 없었지만 기본 이벤트를 비공개로 유지하는 한 이벤트 대리자 사용을 보호하는 것이 합리적입니다. 그러나 추적해야하는 여러 이벤트 구독 또는 하위 구독과 같은 추가 / 제거 처리기에서 발생해야하는보다 구체적인 스레드 동기화가있는 경우에는 제대로 작동하지 않습니다. 그러나 어떤 경우에도 경고를 제거합니다.
Jeremy

-17

-=를 사용하는 대신 = null로 설정하십시오.


5
remove이벤트 의 메서드는 모든 핸들러를 제거하는 것이 아니라 제거를 요청한 핸들러를 제거해야합니다.
Servy

하나만 추가 한 경우 실제로 하나만 제거하면 모두 제거됩니다. 이 특정 resharper 메시지에만 모든 경우에 사용한다고 말하는 것은 아닙니다.
Jonathan Beresford

6
하지만 당신은 하지 않는 대리자는 항상 호출 목록에있는 유일한 항목을 제거하는 것을 알고있다. 이것은 올바른 작업 코드를 특정 상황에서 우연히 작동하지만 대부분의 경우 비정상적이고 진단하기 어려운 잘못된 코드로 변환하여 resharper 메시지를 사라지게합니다.
Servy

-17은 "잠재적 인"답변을 작성하는 데 시간을 할애하는 사람에게는 벌칙이 너무 가혹하기 때문에 나는 이것을 찬성해야했습니다. 부정확하더라도 수동적이지 않은 경우 +1.
John C
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.