"Віднімання делегата має непередбачуваний результат" в 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/… (що стосується лише списків делегатів)
груд

Відповіді:


134

Не бійтеся! Перша частина попередження ReSharper стосується лише видалення списків делегатів. У своєму коді ви завжди видаляєте одного делегата. У другій частині йдеться про впорядкування делегатів після видалення дубліката делегата. Подія не гарантує замовлення виконання для своїх підписників, тому воно теж не впливає на вас.

Оскільки вищевказана механіка може призвести до непередбачуваних результатів, ReSharper видає попередження, коли стикається з оператором віднімання делегата.

ReSharper видає це застереження, оскільки віднімання делегатів для багатоадресних передач може мати добутки, але це не повністю засуджує цю функцію мови. На щастя, ці ґазди є у бахромах, і ви навряд чи зможете зіткнутися з ними, якщо ви просто інструментуєте прості події. Немає кращого способу реалізації власних add/ removeобробників, ви просто повинні помітити.

Я б запропонував знизити рівень попередження ReSharper для цього повідомлення на "Підказку", щоб ви не отримали десенсибілізацію їх попереджень, які зазвичай корисні.


66
Я думаю, що R # погано називати результати "непередбачуваними". Вони дуже чітко вказані. "Не те, що може передбачити користувач", ні в якому разі не те саме, що "непередбачуване". (Це також невірно говорити , що каркасні .NET визначає перевантажені -. Це запечені в C # компілятор Delegateніяк НЕ перевантажувати +і -.)
Jon тарілочках

6
@Jon: Я згоден. Напевно, всі звикли до високої планки Microsoft, встановленої для себе. Рівень польської високий настільки високий, що так багато речей у світі .NET, що змушують вас «потрапити в яму успіху», зустрічаючи мовну особливість, яка є просто швидкою прогулянкою поруч з ямою, де є Шанс, який ви можете пропустити, дехто вважає, що він дрімає, і вимагає прикмети PIT OF SUCCESS IS THAT WAY --->.
Аллон Гуралнек

5
@AllonGuralnek: З іншого боку, коли ви востаннє чули про те, що хтось насправді мав проблеми через це?
Джон Скіт

5
@Jon: Чув про проблему з цим? Я навіть не знав про таку поведінку до того, як було поставлено це питання.
Аллон Гуралнек

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;

Використання суми або віднімання делегата небезпечно тим, що ви можете втратити події при простому призначенні делегата (відповідно до декларації, розробник не може зробити висновок, що це делегат Multicast, як коли він оголошений як подія). Для прикладу, якщо зазначене в цьому питанні властивість не було позначено як подія, код нижче буде вважати, що два перші завдання будуть ВЗАЄМО, оскільки хтось просто призначив делегата (що також дійсно!).

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

При призначенні Method3 я повністю втратив дві початкові підписки. Використання подій дозволить уникнути цієї проблеми і одночасно видалити попередження ReSharper.


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

-17

встановіть його на = null замість використання - =


5
removeметод події не слід видалити всі обробник, а обробник , що було запропоновано бути видалений.
Сервіс

якщо він додав лише один, тоді, якщо він видаляє лише один, він видалив їх усіх. Я не кажу, щоб використовувати його у всіх випадках лише для цього конкретного повідомлення про повторне формування.
Джонатан Бересфорд

6
Але ви не знаєте, що делегат завжди видаляє єдиний елемент зі списку викликів. Це дозволяє змусити повідомлення про повторний перехід, перетворивши правильний робочий код у неправильний зламаний код, який, за певних обставин, випадково спрацює, але в багатьох випадках це буде невдалим і важким для діагностики способом.
Сервіс

Мені довелося це схвалити, оскільки -17 занадто суворо покарає для того, щоб хтось витратив час, щоб написати "потенційну" відповідь. +1 за те, що ви не пасивні, навіть якщо ви помилилися.
Джон С
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.