“Delegar subtração tem resultado imprevisível” em ReSharper / C #?

124

Ao usar os problemas do myDelegate -= eventHandlerReSharper (versão 6):

Delegar subtração tem resultado imprevisível

O racional por trás disso é explicado pelo JetBrains aqui . A explicação faz sentido e, depois de ler, duvido de todos os meus usos -nos delegados.

Como, então ,

  • posso gravar um evento não automático sem deixar o ReSharper mal-humorado?
  • ou, existe uma maneira melhor e / ou "correta" de implementar isso?
  • ou posso simplesmente ignorar o ReSharper?

Aqui está o código simplificado:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

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

fonte
Mono dá o mesmo aviso. Aqui está a descrição do problema do R # confluence.jetbrains.com/display/ReSharper/… (que se aplica apenas às listas de delegados) #
thoredge

Respostas:

134

Não tenha medo! A primeira parte do aviso do ReSharper se aplica apenas à remoção de listas de delegados. No seu código, você está sempre removendo um único delegado. A segunda parte fala sobre a ordem dos delegados após a remoção de um delegado duplicado. Um evento não garante uma ordem de execução para seus assinantes, portanto também não afeta você.

Como a mecânica acima pode levar a resultados imprevisíveis, o ReSharper emite um aviso sempre que encontra um operador de subtração delegado.

O ReSharper está emitindo esse aviso porque a subtração de delegação multicast pode ter problemas, não está condenando totalmente o recurso de idioma. Felizmente, essas dicas estão em casos extremos e é improvável que você as encontre se estiver apenas instrumentando eventos simples. Não há melhor maneira de implementar seus próprios add/ removemanipuladores, você só precisa prestar atenção.

Eu sugiro que você rebaixe o nível de aviso do ReSharper para essa mensagem para "Dica", para que você não seja dessensibilizado com os avisos deles, que geralmente são úteis.

Allon Guralnek
fonte
66
Eu acho que é ruim do R # chamar os resultados de "imprevisíveis". Eles são claramente especificados. "Não é o que o usuário pode prever" não é o mesmo que "imprevisível". (Também é impreciso dizer que os define .NET framework sobrecargas. - É assados no compilador C # Delegateque não sobrecarregue +e -.)
Jon Skeet
6
@ Jon: eu concordo. Acho que todo mundo se acostumou com o alto padrão que a Microsoft estabeleceu para si. O nível de polonês é tão alto quanto é, com tantas coisas no mundo .NET que fazem você "cair no poço do sucesso", encontrando um recurso de linguagem que é uma mera caminhada rápida ao lado do poço, onde há um A chance que você pode perder é considerada por alguns como chocante e merece uma placa dizendo PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek
5
@AllonGuralnek: Por outro lado, quando foi a última vez que você ouviu falar de alguém realmente tendo um problema devido a isso?
precisa
5
@ Jon: Ouvi falar de um problema com isso? Eu nem sabia sobre esse comportamento antes desta pergunta ser publicada.
Allon Guralnek
2
É curioso que o R # avise sobre subtração de delegação, mas não avise sobre implementações comuns de eventos que têm exatamente o mesmo problema . O principal problema é que o .net usa um único Delegate.Combineque "aplaina" os delegados de difusão seletiva; portanto, se for dado aos delegados [X, Y] e Z, não será possível determinar se o resultado deve ser [X, Y, Z] ou [[ X, Y], Z] (o último delegado mantendo o [X,Y]delegado como seu Targete o Invokemétodo desse delegado como seu Method).
Supercat 01/02
28

Você não deve usar delegados diretamente para somar ou subtrair. Em vez disso, seu campo

MyHandler _myEvent;

Em vez disso, também deve ser declarado como um evento. Isso resolverá o problema sem arriscar sua solução e ainda terá o benefício do uso de eventos.

event MyHandler _myEvent;

O uso da soma ou subtração do delegado é perigoso porque você pode perder eventos ao simplesmente atribuir o delegado (conforme declaração, o desenvolvedor não inferirá diretamente que este é um delegado de difusão seletiva como quando é declarado como um evento). Apenas para exemplificar, se a propriedade mencionada nesta pergunta não foi sinalizada como um evento, o código abaixo fará com que as duas primeiras atribuições sejam PERDIDAS, porque alguém simplesmente atribuiu ao delegado (o que também é válido!).

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

Ao atribuir o Method3, perdi completamente as duas assinaturas iniciais. O uso do evento evitará esse problema e, ao mesmo tempo, removerá o aviso ReSharper.

blimac
fonte
Eu nunca pensei em fazer dessa maneira, mas faz sentido proteger o uso de delegado de evento, desde que você mantenha esse evento subjacente privado. No entanto, isso não funciona bem quando há uma sincronização de encadeamento mais específica que deve ocorrer nos manipuladores de adição / remoção, como várias assinaturas de eventos ou sub-assinaturas que precisam ser rastreadas também. No entanto, em qualquer caso, ele remove os avisos.
Jeremy
-17

defina-o como = null em vez de usar - =

Jonathan Beresford
fonte
5
o removemétodo de um evento não deve remover todos os manipuladores, mas o manipulador que foi solicitado a ser removido.
Servy
se ele adicionou apenas um, se ele remove apenas um em efeito, ele removeu todos eles. Não estou dizendo para usá-lo em todos os casos apenas para esta mensagem específica de novo compartilhador.
Jonathan Beresford
6
Mas você não sabe que o delegado está sempre removendo o único item da lista de chamadas. Isso está fazendo com que a mensagem do novo compartilhador desapareça, transformando o código de trabalho correto em código quebrado incorreto que, em certas circunstâncias, funcionará coincidentemente, mas que em muitos casos será quebrado de maneiras incomuns e difíceis de diagnosticar.
Servy
Eu tive que aprovar isso porque -17 é uma penalidade muito severa para alguém dedicar um tempo para escrever uma resposta "potencial". +1 por não ser passivo, mesmo se você estiver incorreto.
John C