"¿La resta del delegado tiene un resultado impredecible" en ReSharper / C #?

124

Cuando se utilizan problemas de myDelegate -= eventHandlerReSharper (versión 6):

La resta del delegado tiene un resultado impredecible

El racional detrás de esto se explica por JetBrains aquí . La explicación tiene sentido y, después de leerla, dudo de todos mis usos de -los delegados.

¿Cómo, pues ,

  • ¿Puedo escribir un evento no automático sin hacer que ReSharper esté malhumorado?
  • o, ¿hay una manera mejor y / o "correcta" de implementar esto?
  • o, ¿puedo ignorar ReSharper?

Aquí está el 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
    }
}

fuente
Mono da la misma advertencia. Aquí está la descripción de R # del problema confluence.jetbrains.com/display/ReSharper/… (que solo se aplica a las listas de delegados)
Thoredge

Respuestas:

134

¡No tengas miedo! La primera parte de la advertencia de ReSharper solo se aplica a la eliminación de listas de delegados. En su código, siempre está eliminando un solo delegado. La segunda parte habla sobre el pedido de delegados después de que se eliminó un delegado duplicado. Un evento no garantiza un orden de ejecución para sus suscriptores, por lo que tampoco te afecta.

Dado que la mecánica anterior puede conducir a resultados impredecibles, ReSharper emite una advertencia cada vez que encuentra un operador de resta delegado.

ReSharper está emitiendo esta advertencia porque la sustracción de delegado de multidifusión puede tener problemas, no está condenando por completo esa característica del lenguaje. Afortunadamente, esas trampas están en casos marginales y es poco probable que las encuentres si solo estás instrumentando eventos simples. No hay manera mejor para poner en práctica sus propias add/ removemanipuladores, solo tienes que tomar nota.

Sugeriría degradar el nivel de advertencia de ReSharper para ese mensaje a "Sugerencia" para que no se vuelva insensible a sus advertencias, que generalmente son útiles.

Allon Guralnek
fuente
66
Creo que es malo de R # llamar a los resultados "impredecibles". Están muy claramente especificados. "No es lo que el usuario podría predecir" no es de ninguna manera lo mismo que "impredecible". (También es incorrecto decir que .NET Framework define sobrecargas, está integrado en el compilador de C #. DelegateNo se sobrecarga +y -.)
Jon Skeet
66
@ Jon: estoy de acuerdo. Supongo que todos se acostumbraron a la barra alta que Microsoft estableció para sí misma. El nivel de pulido es tan alto como es, con tantas cosas en el mundo .NET que te hacen "caer en el pozo del éxito", encontrando una característica del lenguaje que es un simple paseo rápido al lado del pozo donde hay un Es probable que algunos lo consideren desagradable y justifica un letrero que diga PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek
55
@AllonGuralnek: Por otro lado, ¿cuándo fue la última vez que escuchó que alguien realmente tenía un problema debido a esto?
Jon Skeet
55
@ Jon: ¿Has oído hablar de un problema? Ni siquiera sabía sobre este comportamiento antes de que se publicara esta pregunta.
Allon Guralnek
2
Es curioso que R # advierta sobre la resta de delegados, pero no advierta sobre las implementaciones comunes de eventos que tienen exactamente el mismo problema . El problema central es que .net usa un único Delegate.Combineque "aplana" a los delegados de multidifusión, por lo que si se les da delegados [X, Y] y Z, no puede decir si el resultado debería ser [X, Y, Z] o [[ X, Y], Z] (el último delegado mantiene al [X,Y]delegado como su Target, y el Invokemétodo de ese delegado como su Method).
supercat
28

No debe utilizar directamente delegados para sumar o restar. En cambio tu campo

MyHandler _myEvent;

En su lugar, también debe declararse como un evento. Esto resolverá el problema sin arriesgar su solución y aún tendrá el beneficio del uso de eventos.

event MyHandler _myEvent;

El uso de la suma o resta del delegado es peligroso porque puede perder eventos al simplemente asignar el delegado (según la declaración, el desarrollador no inferirá directamente que se trata de un delegado de multidifusión como cuando se declara como un evento). Solo para ejemplificar, si la propiedad mencionada en esta pregunta no se marcó como un evento, el código a continuación hará que las dos primeras asignaciones se PERDERAN, porque alguien simplemente lo asignó al delegado (¡lo cual también es válido!).

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

Al asignar el Método 3, perdí por completo las dos suscripciones iniciales. El uso de eventos evitará este problema y al mismo tiempo eliminará la advertencia de ReSharper.

blimac
fuente
Nunca pensé en hacerlo de esta manera, pero tiene sentido proteger el uso del delegado de eventos siempre que mantenga privado ese evento subyacente. Sin embargo, esto no funciona bien cuando hay una sincronización de subprocesos más específica que debe suceder en los controladores de agregar / quitar, como suscripciones de eventos múltiples o suscripciones secundarias que también deben rastrearse. Sin embargo, en cualquier caso, elimina las advertencias.
Jeremy
-17

configúrelo como = nulo en lugar de usar - =

Jonathan Beresford
fuente
55
El removemétodo de un evento no debe eliminar todos los controladores, sino el controlador que se solicitó eliminar.
Servy
si solo ha agregado uno, entonces si solo elimina uno en efecto, los eliminará a todos. No digo que lo use en todos los casos solo para este mensaje específico de resharper.
Jonathan Beresford
66
Pero no sabe que el delegado siempre elimina el único elemento en la lista de invocación. Esto está haciendo que el mensaje de intercambio desaparezca al convertir el código de trabajo correcto en un código roto incorrecto que, en ciertas circunstancias, funcionará por coincidencia, pero que se romperá de manera inusual y difícil de diagnosticar en muchos casos.
Servy
Tuve que votar esto porque -17 es una penalización demasiado dura para alguien que se toma el tiempo de escribir una respuesta "potencial". +1 por no ser pasivo, incluso si fueras incorrecto.
John C