«Вычитание делегата привело к непредсказуемому результату» в 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
    }
}

источник
Моно дает такое же предупреждение. Вот описание проблемы с помощью R # confluence.jetbrains.com/display/ReSharper/… (применимо только к спискам делегатов)
thoredge

Ответы:

134

Не бойся! Первая часть предупреждения ReSharper касается только удаления списков делегатов. В вашем коде вы всегда удаляете одного делегата. Во второй части рассказывается об упорядочивании делегатов после удаления дублирующего делегата. Событие не гарантирует порядок выполнения для своих подписчиков, поэтому на вас оно тоже не влияет.

Поскольку описанный выше механизм может привести к непредсказуемым результатам, ReSharper выдает предупреждение всякий раз, когда встречает оператор вычитания делегата.

ReSharper выдает это предупреждение, потому что вычитание многоадресного делегата может иметь проблемы, и он не осуждает полностью эту языковую функцию. К счастью, эти подводные камни бывают в крайних случаях, и вы вряд ли столкнетесь с ними, если вы просто инструментируете простые события. Нет лучшего способа реализовать свои собственные add/ removeобработчики, вы просто должны заметить.

Я бы посоветовал понизить уровень предупреждений ReSharper для этого сообщения до «Подсказка», чтобы вы не теряли чувствительность к их предупреждениям, которые обычно полезны.

Аллон Гуралнек
источник
67
Я считаю, что в R # плохо называть результаты «непредсказуемыми». Они очень четко указаны. «Не то, что пользователь мог бы предсказать» - это ни в коем случае не то же самое, что «непредсказуемо». (Это также неверно говорить , что каркасные .NET определяет перегруженные -. Это запеченные в C # компилятор Delegateникак не перегружать +и -.)
Jon тарелочкам
6
@Jon: Я согласен. Думаю, все привыкли к высокой планке, которую Microsoft поставила для себя. Уровень полировки настолько высок, как он есть, с таким количеством вещей в мире .NET, которые заставляют вас «упасть в яму успеха», столкнувшись с языковой функцией, которая представляет собой простую оживленную прогулку рядом с ямой, где есть Вероятность того, что вы можете пропустить, некоторые считают неприятным и требуют таблички с надписью PIT OF SUCCESS IS THAT WAY --->.
Аллон Гуралнек
5
@AllonGuralnek: С другой стороны, когда вы в последний раз слышали, чтобы у кого-то действительно возникла проблема из-за этого?
Джон Скит,
5
@Jon: Слышал о проблеме? Я даже не знал об этом до того, как был опубликован этот вопрос.
Allon Guralnek
2
Любопытно, что R # предупреждает о вычитании делегата, но не предупреждает об общих реализациях событий, имеющих одну и ту же проблему . Основная проблема заключается в том, что .net использует сингл, Delegate.Combineкоторый «выравнивает» многоадресные делегаты, поэтому, если ему даны делегаты [X, Y] и Z, он не может определить, должен ли результат быть [X, Y, Z] или [[ X, Y], Z] (последний делегат хранит [X,Y]делегат как свой Target, а Invokeметод этого делегата - как свой Method).
supercat 01
28

Вы не должны напрямую использовать делегаты для суммирования или вычитания. Вместо твоего поля

MyHandler _myEvent;

Вместо этого следует также объявить как событие. Это решит проблему, не рискуя своим решением, и при этом останется преимущество использования событий.

event MyHandler _myEvent;

Использование делегата суммы или вычитания опасно, потому что вы можете потерять события при простом назначении делегата (согласно объявлению, разработчик не будет напрямую делать вывод, что это делегат Multicast, как когда он объявлен как событие). Чтобы проиллюстрировать это, если свойство, упомянутое в этом вопросе, не было помечено как событие, в приведенном ниже коде два первых назначения будут LOST, потому что кто-то просто назначен делегату (что также верно!).

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

При назначении Method3 я полностью потерял две начальные подписки. Использование события позволит избежать этой проблемы и в то же время удалить предупреждение ReSharper.

blimac
источник
Я никогда не думал о том, чтобы сделать это таким образом, но имеет смысл защитить использование делегата события, если вы сохраняете это базовое событие закрытым. Однако это не работает, когда есть более конкретная синхронизация потоков, которая должна происходить в обработчиках добавления / удаления, например, несколько подписок на события или подписок, которые также необходимо отслеживать. Однако в любом случае он снимает предупреждения.
Джереми
-17

установите для него значение = null вместо использования - =

Джонатан Бересфорд
источник
5
removeметод события не следует удалить все обработчик, а обработчик , что было предложено быть удален.
Servy
если он добавил только один, то, если он удаляет только один, по сути, он удаляет их все. Я не говорю, что используйте его во всех случаях только для этого конкретного сообщения resharper.
Джонатан Бересфорд,
6
Но вы не знаете, что делегат всегда удаляет единственный элемент в списке вызовов. Это заставляет сообщение resharper исчезнуть, превратив правильный рабочий код в неправильный сломанный код, который при определенных обстоятельствах будет работать по совпадению, но во многих случаях это будет ломаться необычным и трудным для диагностики способами.
Servy
Мне пришлось проголосовать за это, потому что -17 - слишком суровое наказание для кого-то, кто нашел время написать «потенциальный» ответ. +1 за то, что вы не были пассивными, даже если вы ошибались.
Джон К.