solicitud que puede para nulo los linea despues depositado cruzan como cheques cheque certificado cancelar cancelacion banco anulados anulado c# events null

c# - para - se puede cancelar un cheque despues de depositado



¿Por qué C#requiere que escribas un cheque nulo cada vez que dispares un evento? (9)

Esto me parece extraño: VB.NET maneja la comprobación nula implícitamente a través de su palabra clave RaiseEvent . Parece que aumenta considerablemente la cantidad de repeticiones alrededor de los eventos y no veo qué beneficio proporciona.

Estoy seguro de que los diseñadores de idiomas tenían una buena razón para hacer esto ... pero tengo curiosidad por saber si alguien sabe por qué.


Debe considerar qué código se requeriría si configurar la tubería para elevar el evento en primer lugar sería costoso (como SystemEvents) o al preparar los argumentos del evento sería costoso (como el evento Paint).

El estilo de Visual Basic de manejo de eventos no le permite posponer el costo de apoyar dicho evento. No puede anular los agregadores de agregar / quitar para retrasar la instalación de la tubería costosa. Y no puede descubrir que puede que no haya ningún controlador de eventos suscrito, por lo que quemar los ciclos para preparar los argumentos del evento es una pérdida de tiempo.

No es un problema en C #. Intercambio clásico entre conveniencia y control.


Es sin duda un punto de molestia.

Cuando escribe un código que accede a un evento similar a un campo dentro de una clase, en realidad está accediendo al campo en sí mismo (módulo algunos cambios en C # 4; no vayamos por el momento).

Entonces, las opciones serían:

  • Invocaciones de eventos similares a campos de casos especiales para que en realidad no se refirieran directamente al campo, sino que agregaran un contenedor
  • Maneja todas las invocaciones de los delegados de manera diferente, de modo que:

    Action<string> x = null; x();

    No lanzaría una excepción.

Por supuesto, para los delegados no nulos (y eventos) ambas opciones plantean un problema:

Func<int> x = null; int y = x();

¿Debería eso devolver en silencio 0? (El valor predeterminado de un int .) O es en realidad enmascarar un error (más probable). Sería algo inconsistente hacer que ignore en silencio el hecho de que está intentando invocar a un delegado nulo. Sería aún más extraño en este caso, que no utiliza el azúcar sintáctico de C #:

Func<int> x = null; int y = x.Invoke();

Básicamente, las cosas se vuelven difíciles e inconsistentes con el resto del idioma, casi lo que sea que hagas. Tampoco me gusta, pero no estoy seguro de lo que podría ser una solución práctica pero consistente ...


La razón realmente se reduce a C # que le da más control. En C # no tiene que hacer la comprobación null si así lo elige. En el siguiente código, MyEvent nunca puede ser null ¿por qué molestarse en realizar la comprobación?

public class EventTest { private event EventHandler MyEvent = delegate { Console.WriteLine("Hello World"); } public void Test() { MyEvent(this, new EventArgs()); } }


Los métodos de extensión proporcionan una manera muy fresca, para evitar esto. Considere el siguiente código:

static public class Extensions { public static void Raise(this EventHandler handler, object sender) { Raise(handler, sender, EventArgs.Empty); } public static void Raise(this EventHandler handler, object sender, EventArgs args) { if (handler != null) handler(sender, args); } public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs { if (handler != null) handler(sender, args); } }

Ahora puedes simplemente hacer esto:

class Test { public event EventHandler SomeEvent; public void DoSomething() { SomeEvent.Raise(this); } }

Sin embargo, como otros ya han mencionado, debe tener en cuenta la posible condición de carrera en escenarios de subprocesos múltiples.


No sé realmente por qué se hace esto, pero hay una variación de un patrón de Objeto nulo específicamente para los delegados:

private event EventHandler Foo = (sender, args) => {};

De esta forma, puedes invocar libremente a Foo sin haber comprobado nunca el null .


Porque RaiseEvent lleva una cierta sobrecarga.

Siempre hay una compensación entre el control y la facilidad de uso.

  • VB.Net: facilidad de uso,
  • C #: más control como VB
  • C ++: incluso más control, menos guía, más fácil dispararte en el pie

Solemos solucionar esto declarando nuestros eventos de esta manera:

public event EventHandler<FooEventArgs> Foo = delegate { };

Esto tiene dos ventajas. La primera es que no tenemos cheque por nulo. La segunda es que evitamos el problema de la sección crítica que está omnipresente en la activación de eventos típica:

// old, busted code that happens to work most of the time since // a lot of code never unsubscribes from events protected virtual void OnFoo(FooEventArgs e) { // two threads, first checks for null and succeeds and enters if (Foo != null) { // second thread removes event handler now, leaving Foo = null // first thread resumes and crashes. Foo(this, e); } } // proper thread-safe code protected virtual void OnFoo(FooEventArgs e) { EventHandler<FooEventArgs> handler = Foo; if (handler != null) handler(this, e); }

Pero con la inicialización automática de Foo a un delegado vacío, nunca es necesario realizar ninguna comprobación y el código es automáticamente seguro para subprocesos y es más fácil de leer para iniciar:

protected virtual void OnFoo(FooEventArgs e) { Foo(this, e); // always good }

Con mis disculpas a Pat Morita en Karate Kid, "La mejor manera de evitar el nulo no es tener una".

En cuanto al por qué, C # no te mata tanto como VB. Aunque la palabra clave del evento oculta la mayoría de los detalles de implementación de los delegados de multidifusión , le da un mejor control que VB.


Tenga en cuenta que a partir de C # 6, el lenguaje ahora proporciona una sintaxis concisa para realizar esta comprobación nula de manera conveniente. P.ej:

public event EventHandler SomeEvent; private void M() { // raise the event: SomeEvent?.Invoke(this, EventArgs.Empty); }

Ver Operador Condicional Nulo


Edición: como señala el OP, esta respuesta no aborda el cuerpo de la pregunta. Sin embargo, algunos pueden encontrarlo útil porque proporciona una respuesta para el título de la pregunta (cuando se toma solo):

¿Por qué C # requiere que escribas un cheque nulo cada vez que dispares un evento?

También proporciona un contexto para la intención del cuerpo de la pregunta que algunos pueden encontrar útil. Entonces, por esas razones y este consejo sobre Meta, dejaré que esta respuesta se mantenga.

Texto original:

En su artículo de MSDN Cómo publicar eventos que se ajustan a las pautas de .NET Framework (Guía de programación de C #) (Visual Studio 2013) , Microsoft incluye el siguiente comentario en su ejemplo:

// Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised.

Aquí hay un extracto más grande del código de ejemplo de Microsoft que le da contexto a ese comentario.

// Wrap event invocations inside a protected virtual method // to allow derived classes to override the event invocation behavior protected virtual void OnRaiseCustomEvent(CustomEventArgs e) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. EventHandler<CustomEventArgs> handler = RaiseCustomEvent; // Event will be null if there are no subscribers if (handler != null) { // Format the string to send inside the CustomEventArgs parameter e.Message += String.Format(" at {0}", DateTime.Now.ToString()); // Use the () operator to raise the event. handler(this, e); } }