Делегираното изваждане има непредвидим резултат в 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
Mono дава същото предупреждение. Ето описанието на R# на проблема confluence.jetbrains. com/display/ReSharper/ (които се отнасят само за списъци с делегати)   -  person thoredge    schedule 19.10.2015


Отговори (3)


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

Тъй като горната механика може да доведе до непредвидими резултати, ReSharper издава предупреждение всеки път, когато срещне оператор за изваждане на делегат.

ReSharper издава това предупреждение, тъй като изваждането на делегиране на мултикаст може да има проблеми, но не осъжда изцяло тази езикова функция. За щастие тези проблеми са в странични случаи и е малко вероятно да ги срещнете, ако просто инструментирате прости събития. Няма по-добър начин да внедрите свои собствени add/remove манипулатори, просто трябва да обърнете внимание.

Бих предложил да понижите нивото на предупреждение на ReSharper за това съобщение до „Подсказка“, за да не станете десенсибилизирани към техните предупреждения, които обикновено са полезни.

person Allon Guralnek    schedule 24.06.2012
comment
Мисля, че е лошо от R# да нарича резултатите непредвидими. Те са много ясно посочени. Не това, което потребителят може да предвиди, по никакъв начин не е същото като непредвидимо. (Също така е неточно да се каже, че .NET рамката дефинира претоварвания - тя е запечатана в C# компилатора. Delegate не претоварва + и -.) - person Jon Skeet; 24.06.2012
comment

Вие търсите това: тествах го и работи

    CGFloat sectionFooterHeight = 40;
    CGFloat tableViewHeight = self.tableView.frame.size.height;

    if (scrollView.contentOffset.y=tableViewHeight) {
        scrollView.contentInset = UIEdgeInsetsMake(0, 0,-scrollView.contentOffset.y, 0);
    } else if (scrollView.contentOffset.y>=sectionFooterHeight+self.tableView.frame.size.height) {
        scrollView.contentInset = UIEdgeInsetsMake(0, 0,-sectionFooterHeight, 0);
    }

- 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
Проверете wiki на 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;

Използването на сума или изваждане на делегат е опасно, защото можете да загубите събития, когато просто присвоите делегата (съгласно декларацията, разработчикът няма да заключи директно, че това е Multicast делегат, както когато е деклариран като събитие). Само за пример, ако свойството, споменато в този въпрос, не е било маркирано като събитие, кодът по-долу ще представи първите две присвоявания като ЗАГУБЕНИ, защото някой просто е присвоил на делегата (което също е валидно!).

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

При присвояването на метод 3 загубих напълно двата първоначални абонамента. Използването на събития ще избегне този проблем и в същото време ще премахне предупреждението 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