Вычитание делегата дает непредсказуемый результат в ReSharper / C #?

При использовании myDelegate -= eventHandler ReSharper (версия 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
    }
}

person Community    schedule 24.06.2012    source источник
comment
Моно дает такое же предупреждение. Вот описание проблемы с помощью R # confluence.jetbrains. com / display / ReSharper / (которые применяются только к спискам делегатов)   -  person thoredge    schedule 19.10.2015


Ответы (3)


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

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

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

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

person Allon Guralnek    schedule 24.06.2012
comment
Я считаю, что в R # плохо называть результаты непредсказуемыми. Они очень четко указаны. То, что пользователь может предсказать, ни в коем случае не значит непредсказуемо. (Также неверно сказать, что платформа .NET определяет перегрузки - она ​​встроена в компилятор C #. Delegate не перегружает + и -.) - person Jon Skeet; 24.06.2012
comment
@Jon: Я согласен. Думаю, все привыкли к высокой планке, которую Microsoft поставила для себя. Уровень полировки настолько высок, насколько он есть, с таким количеством вещей в мире .NET, которые заставляют вас упасть в яму успеха, столкнувшись с языковой функцией, которая представляет собой простую оживленную прогулку рядом с ямой, где есть шанс, что вы может пропустить, это считается некоторыми раздражающим и требует таблички с надписью PIT OF SUCCESS IS THAT WAY --->. - person Allon Guralnek; 25.06.2012
comment
@AllonGuralnek: С другой стороны, когда вы в последний раз слышали, чтобы у кого-то действительно возникла проблема из-за этого? - person Jon Skeet; 25.06.2012
comment
@Jon: Слышал о проблеме? Я даже не знал об этом поведении до того, как был опубликован этот вопрос. - person Allon Guralnek; 25.06.2012
comment
Что ж, мне кажется, это хороший ответ. (ReSharper не предупреждает о нормальном использовании делегатов + = / - =, я думаю, он может определить, что они не относятся к списку?) - person ; 25.06.2012
comment
Проверьте вики JetBrains: confluence.jetbrains.net/display/ReSharper/ Вот пример хитрых случаев с вычитанием делегата - person Evgeny Pasynkov; 05.07.2012
comment
Любопытно, что R # предупреждает о вычитании делегата, но не предупреждает об общих реализациях событий, которые имеют одну и ту же проблему. Основная проблема заключается в том, что .net использует один Delegate.Combine, который сглаживает многоадресные делегаты, поэтому, если ему даны делегаты [X, Y] и Z, он не может определить, должен ли результат быть [X, Y, Z] или [[X , Y], Z] (последний делегат содержит делегат [X,Y] в качестве своего Target, а метод Invoke этого делегата - в качестве своего Method). - person supercat; 02.02.2013
comment
@Allon Guralnek: вы можете легко добавлять и вычитать списки делегатов с помощью операторов добавления / удаления, поскольку значение в этих методах имеет тип MyHandler, который является делегатом и, таким образом, может легко содержать списки делегатов. - person mmmmmmmm; 11.04.2013
comment
Я думаю, что проблема возникает редко, поскольку в большинстве случаев вы удаляете то же самое, что добавляете. Поэтому, если вы добавляете список делегатов BC, вы также удаляете тот же список BC, а не CB. И в этом случае (как я понял из описания R #) делегат - работает. Тем не менее, я бы предпочел идеальное решение :-) - person mmmmmmmm; 11.04.2013
comment
Я думаю, что ответ blimacs должен быть принятым, потому что он решает проблему более чистым способом (в отличие от понижения уровня предупреждения ReSharper). - person mike; 15.08.2018

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

MyHandler _myEvent;

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

event MyHandler _myEvent;

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

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

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

person blimac    schedule 28.10.2015
comment
Я никогда не думал о том, чтобы сделать это таким образом, но имеет смысл защитить использование делегата события, если вы сохраняете это базовое событие закрытым. Однако это не работает, когда есть более конкретная синхронизация потоков, которая должна происходить в обработчиках добавления / удаления, таких как множественные подписки на события или подписки, которые также необходимо отслеживать. Однако в любом случае он снимает предупреждения. - person Jeremy; 01.12.2017

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

person Jonathan Beresford    schedule 23.02.2015
comment
remove метод события должен удалять не все обработчики, а обработчик, который был запрошен для удаления. - person Servy; 23.02.2015
comment
если он добавил только один, то, если он удаляет только один, по сути, он удаляет их все. Я не говорю, что используйте его во всех случаях только для этого конкретного сообщения resharper. - person Jonathan Beresford; 24.02.2015
comment
Но вы не знаете, что делегат всегда удаляет единственный элемент в списке вызовов. Это заставляет сообщение resharper исчезнуть, превратив правильный рабочий код в неправильный сломанный код, который при определенных обстоятельствах будет работать по совпадению, но во многих случаях это будет ломаться необычными и трудно диагностируемыми способами. - person Servy; 24.02.2015
comment
Мне пришлось проголосовать за это, потому что -17 - слишком суровое наказание для того, чтобы кто-то нашел время, чтобы написать потенциальный ответ. +1 за то, что вы не были пассивными, даже если вы ошибались. - person John C; 06.02.2020