2010-02-15 14 views
32

Piccolo domanda su C design # lingua :))del linguaggio C# Design: implementazione dell'interfaccia esplicita di un evento

Se avessi un'interfaccia simile a questo:

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

E 'possibile implementare in modo esplicito tale interfaccia utilizzando C# 3.0 proprietà auto-implementato:

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

Ma se dovessi un evento nell'interfaccia:

interface IFoo { 
    event EventHandler Event; 
} 

E cercando di attuare in modo esplicito utilizzando campo simile evento:

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

mi metterò il seguente errore del compilatore:

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

Credo che gli eventi sul campo come è il una sorta di dualismo per le proprietà auto-implementate.

Quindi la mia domanda è: qual è il motivo di progettazione per tale restrizione fatto?

+1

Sono sicuro che la spiegazione di Andreas Huber è corretta. Puoi aggirare il problema avendo un altro evento privato che chiami dall'implementazione esplicita. In questo modo otterrete tutte le funzionalità automatiche (thread-safety stuff, ecc.) Dalla funzionalità di eventi di tipo field. Esempio: 'sealed class Foo: IFoo { event EventHandler privateEventFieldLike; event EventHandler IFoo.Event {add {privateEventFieldLike + = value; } remove {privateEventFieldLike - = value; }} } ' –

risposta

26

Credo che potrebbe avere a che fare con il fatto che non si può chiamare un'implementazione dell'interfaccia esplicita dagli altri membri della classe:

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() { } 
} 

Si noti che è possibile chiamare il metodo tramite upcasting all'interfaccia prima: ((I)this).DoIt();. Un po 'brutto ma funziona.

Se gli eventi potrebbero essere implementati in modo esplicito come suggerito da ControlFlow (l'OP), come li aumenteresti effettivamente? Considerare:

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; 
} 

Qui non si può nemmeno alzare l'evento upcasting all'interfaccia prima, perché gli eventi possono essere sollevati solo dall'interno della classe che implementa. Sembra quindi perfettamente logico richiedere la sintassi dell'accessorio per eventi esplicitamente implementati.

+0

Buona risposta. Sembra plausibile per me. Grazie. –

+0

Sono d'accordo.Un evento di tipo field consiste di tre cose: l'accessore 'add', l'accessore' remove' e l'accesso privato al campo generato sottostante del tipo delegato. Tale campo delegato viene utilizzato direttamente quando si utilizza 'MyEvent! = Null' o' local = MyEvent' o 'MyEvent (sender, args)' o così via. Tuttavia, solo gli accessor di 'add' e' remove' fanno parte dell'interfaccia "contract". –

+0

In C# 6.0 (2015) è possibile creare una proprietà automatica che è 'get'-only. È quindi possibile assegnarlo ad un costruttore della stessa classe. Questo assegna davvero al campo sottostante. Per esempio 'sealed class Foo {public int Value {get; } public Foo() {Value = 42; }} '. Se si tenta di farlo con un'implementazione esplicita dell'interfaccia, il compilatore C# _will_ lo consente, ma non è più possibile assegnarlo in un costruttore (il campo di supporto non fa parte del contratto "get'-only interface"). 'classe sigillata Foo: IFoo {int IFoo.Value {get; } public Foo() {}} ' –

27

Interessante domanda. Ho fatto un po 'di ricerche sull'archivio delle note linguistiche e ho scoperto che questa decisione è stata presa il 13 ottobre 1999, ma le note non giustificano la decisione.

Al di sopra della mia testa non vedo alcun motivo teorico o pratico per cui non potremmo avere eventi esplicitamente implementati simili a campi. Né vedo alcun motivo per cui ne abbiamo particolarmente bisogno. Questo potrebbe dover rimanere uno dei misteri dell'ignoto.

+6

Ora questo è il motivo per cui continuo a visitare SO a volte ... Dove altrimenti la gente dovrebbe prendere" Non so "per una buona risposta. – ima

+4

Sembra che il motivo potrebbe essere che non si potrebbe mai generare un tale evento, a meno che gli eventi esplicitamente implementati avessero regole di visibilità diverse rispetto ai metodi esplicitamente implementati, come spiegato nella mia risposta ... –

+1

Penso che la risposta di Andreas Huber sia ragionevole. –

16

Quando si implementa esplicitamente un evento dichiarato in un'interfaccia, è necessario utilizzare manualmente fornire l'aggiunta e la rimozione di accessors di eventi forniti in genere dal compilatore. Il codice di accesso può connettere l'evento di interfaccia a un altro evento della classe o al proprio tipo di delegato.

Ad esempio, questo attiverà l'errore CS0071:

public delegate void MyEvent(object sender); 

interface ITest 
{ 
    event MyEvent Clicked; 
} 

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

Il modo corretto sarebbe:

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() { } 
} 

vedere Compiler Error CS0071

+0

+1 per l'implementazione corretta, tuttavia quella non era la domanda posta. Le persone tendono ad arrivare alla vera carne in fondo alla loro domanda. Un po 'come volevi spiegare PERCHE' hai fatto quello che hai fatto prima di dire ai tuoi genitori che hai ottenuto la detenzione. – Will

+4

+1 Forse non è la risposta esplicita alla domanda, ma questo mi ha appena salvato probabilmente un'ora buona nel cercare di capire un lavoro. –

1

Questo non sarebbe in realtà un pensiero originale da solo.

Tuttavia, ho pensato che avrei potuto rispondere a questo:

"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


Nel capitolo 23 di Introduzione A di programmazione per C#, seconda edizione, Eric Gunnerson ha scritto:

"[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 -=."


Egli continua nelle prossime pagine per commentare comprendente la maggiorazione() e rimuovere() per implementare questo comportamento; essere in grado di scrivere direttamente su questi metodi e le conseguenze dell'allocazione dello spazio di archiviazione per i riferimenti dei delegati non necessari.

Vorrei aggiungere altro, ma rispetto troppo l'autore per farlo senza il suo permesso. Raccomando di trovare una copia di questo libro e consiglierei qualsiasi cosa di Eric Gunnerson in generale (blog, ecc ...)

In ogni caso, spero che questo sia pertinente all'argomento e se è così, spero che appaia chiaro su questo " mistero dell'ignoto "? (Stavo leggendo questo stesso capitolo e ho cercato Stack Overflow per approfondire le considerazioni sulla logica del gestore di eventi durante la creazione di collezioni personalizzate da oggetti personalizzati) - Lo dico solo perché non rivendico alcuna autorità specifica su questo particolare argomento. Io sono solo uno studente in cerca di "illuminazione" me :-)

+0

Non mi piace davvero l'uso dello stesso identificatore per un evento automatico e il suo delegato sottostante. IMHO, l'uso del nome dell'evento avrebbe dovuto essere consentito per l'aggiunta o la rimozione di una sottoscrizione, l'impostazione di un elenco di sottoscrizioni su null o il confronto con null o per invocare un evento, * con la sintassi di invocazione automaticamente incluso il controllo nullo *. Qualsiasi altro uso dovrebbe aver richiesto l'utilizzo di un identificatore per il delegato. – supercat

Problemi correlati