2010-02-15 8 views
32

pequeña pregunta sobre C diseño # idioma :))C Diseño # Idioma: implementación de interfaz explícita de un evento

Si tuviera una interfaz de esta manera:

interface IFoo { 
    int Value { get; set; } 
} 

Es posible implementar explícitamente este tipo de interfaz con C# 3.0 propiedades implementadas automáticamente:

sealed class Foo : IFoo { 
    int IFoo.Value { get; set; } 
} 

Pero si tuviera un evento en la interfaz:

interface IFoo { 
    event EventHandler Event; 
} 

Y tratando de poner en práctica de forma explícita el uso de campo como evento:

sealed class Foo : IFoo { 
    event EventHandler IFoo.Event; 
} 

voy a tener el siguiente error de compilación:

error CS0071: An explicit interface implementation of an event must use event accessor syntax

creo que los eventos de campo, como es el algún tipo de dualismo para las propiedades implementadas automáticamente.

Así que mi pregunta es: cuál es la razón para el diseño de dicha restricción hace?

+1

Estoy seguro de que la explicación de Andreas Huber es correcta. Puede solucionar el problema teniendo otro evento privado al que llame desde la implementación explícita. De esta forma, obtendrá todas las funciones automáticas (material de seguridad de hilos, etc.) de la funcionalidad de eventos de tipo campo. Ejemplo: 'clase sellada Foo: IFoo { evento EventHandler privateEventFieldLike; evento EventHandler IFoo.Event {add {privateEventFieldLike + = value; } remove {privateEventFieldLike - = value; }} } ' –

Respuesta

26

supongo que podría tener que ver con el hecho de que no se puede llamar una implementación de interfaz explícita de otros miembros de la clase:

public interface I 
{ 
    void DoIt(); 
} 

public class C : I 
{ 
    public C() 
    { 
     DoIt(); // error CS0103: The name 'DoIt' does not exist in the current context 
    } 

    void I.DoIt() { } 
} 

Tenga en cuenta que puede llamar al método por upcasting a la interfaz primero: ((I)this).DoIt();. Un poco feo pero funciona.

Si los eventos podrían implementarse explícitamente como ControlFlow (OP) sugerido, ¿cómo los elevaría? Consideremos:

public interface I 
{ 
    event EventHandler SomethingHappened; 
} 

public class C : I 
{ 
    public void OnSomethingHappened() 
    { 
     // Same problem as above 
     SomethingHappened(this, EventArgs.Empty); 
    } 

    event EventHandler I.SomethingHappened; 
} 

Aquí aún no se pueda elevar el evento upcasting a la interfaz en primer lugar, porque los acontecimientos sólo pueden ser planteadas desde dentro de la clase que implementa. Por lo tanto, parece tener perfecto sentido requerir sintaxis de acceso para eventos implementados explícitamente.

+0

Buena respuesta. Me parece plausible. Gracias. –

+0

Estoy de acuerdo.Un evento similar a un campo consta de tres cosas: el acceso directo 'add', el acceso directo' remove' y el acceso privado al campo generado subyacente del tipo de delegado. Ese campo de delegado se usa directamente cuando usa 'MyEvent! = Null' o' local = MyEvent' o 'MyEvent (sender, args)' o más. Sin embargo, solo los accesadores 'add' y' remove' son parte de la interfaz "contract". –

+0

En C# 6.0 (2015), es posible crear una propiedad automática que sea 'get'-only. Entonces es posible asignarlo desde un constructor de la misma clase. Eso realmente le asigna al campo subyacente. Por ejemplo, 'clase sellada Foo {public int Value {get; } public Foo() {Value = 42; }} '. Si intenta hacer esto con la implementación explícita de la interfaz, el compilador de C# lo permitirá, pero ya no podrá asignarlo en un constructor (el campo de respaldo no es parte del contrato de la interfaz 'get'-only ''). 'clase sellada Foo: IFoo {int IFoo.Value {get; } public Foo() {}} ' –

27

pregunta interesante. Hice un poco de hurgar archivo las notas de lengua y descubrí que esta decisión fue tomada el 13 de octubre de 1999, pero las notas no se dé una justificación para la decisión.

De la parte superior de mi cabeza aún no hay razón teórica o práctica por la cual no podríamos tener eventos implementados de manera explícita el terreno similares. Tampoco veo ningún motivo por el que lo necesitemos particularmente. Esto puede tener que seguir siendo uno de los misterios de lo desconocido.

+6

Ahora bien, esta es la razón por la que todavía visito SO a veces ... ¿En qué otro lugar la gente tomaría" No sé "para obtener una buena respuesta. – ima

+4

Parece que la razón podría ser que nunca podría plantear un evento así, a menos que los eventos implementados explícitamente tuvieran reglas de visibilidad diferentes que los métodos implementados explícitamente, como se explica en mi respuesta ... –

+1

Creo que la respuesta de Andreas Huber es razonable. –

16

Al implementar explícitamente un evento que fue declarado en una interfaz, debe utilizar manualmente proporcionar al agregar y quitar descriptores de acceso de eventos que normalmente son proporcionados por el compilador. El código de acceso puede conectar el evento de interfaz a otro evento en su clase o a su propio tipo de delegado.

Por ejemplo, esto desencadenará CS0071 de error:

public delegate void MyEvent(object sender); 

interface ITest 
{ 
    event MyEvent Clicked; 
} 

class Test : Itest 
{ 
    event MyEvent ITest.Clicked; // CS0071 
    public static void Main() { } 
} 

La forma correcta sería:

public delegate void MyEvent(object sender); 

interface ITest 
{ 
    event MyEvent Clicked; 
} 

class Test : Itest 
{ 
    private MyEvent clicked; 

    event MyEvent Itest.Clicked 
    { 
     add 
     { 
      clicked += value; 
     } 

     remove 
     { 
      clicked -= value; 
     } 
    } 

    public static void Main() { } 
} 

ver Compiler Error CS0071

+0

+1 para la implementación correcta, sin embargo esa no fue la pregunta. La gente tiende a llegar a la verdadera carne en el fondo de su pregunta. Algo así como cómo quisiste explicar POR QUÉ hiciste lo que hiciste antes de decirle a tus padres que te arrestaron. – Will

+4

+1 Quizás no sea la respuesta explícita a la pregunta, pero esto me ahorró probablemente una buena hora para tratar de encontrar una solución alternativa. –

1

Esto no sería en realidad un pensamiento original por mí mismo.

Sin embargo, pensé que podría responder a esto:

"Off the top of my head I don't see any theoretical or practical reason why we could not have field-like explicitly implemented events. Nor do I see any reason why we particularly need to. This may have to remain one of the mysteries of the unknown." -Eric Lippert


En el capítulo 23 de la Introducción de un desarrollador de C#, segunda edición, Eric Gunnerson escribió:

"[I]f another class also wanted to be called when the button was clicked, the += operator could be used, like this:

button.Click += new Button.ClickHandler(OtherMethodToCall);

Unfortunately, if the other class wasn't careful, it might do the following:

button.Click = new Button.ClickHandler(OtherMethodToCall);

This would be bad, as it would mean that our ButtonHandler would be unhooked and only the new method would be called."

...

"What is needed is some way of protecting the delegate field so that it is only accessed using += and -=."


Continúa en los próximos páginas a comentar incluyendo el add() y remove() para implementar este comportamiento; poder escribir en esos métodos directamente y la consecuencia de la asignación de almacenamiento para referencias delegadas innecesarias.

Agregaría más, pero respeto al autor demasiado para hacerlo sin su permiso. Recomiendo encontrar una copia de este libro y recomendaría cualquier cosa de Eric Gunnerson en general (blog, etc.)

De todos modos, espero que esto sea relevante para el tema y, de ser así, espero que arroje luz sobre esto ". misterio de lo desconocido "? (Estaba leyendo este mismo capítulo y buscando Stack Overflow para conocer las consideraciones lógicas del controlador de eventos al crear colecciones personalizadas a partir de objetos personalizados) - Solo menciono esto porque no reclamo autoridad específica sobre este tema en particular. No soy más que un estudiante en busca de la "iluminación" yo :-)

+0

Realmente no me gusta el uso del mismo identificador para un evento automático y su delegado subyacente. En mi humilde opinión, el uso del nombre del evento debería haber sido permitido para agregar o eliminar una suscripción, configurar una lista de suscripción para anularla o compararla contra null, o para invocar un evento, * con sintaxis de invocación que incluye automáticamente la verificación nula *. Cualquier otro uso debería haber requerido el uso de un identificador para el delegado. – supercat

Cuestiones relacionadas