2012-03-23 8 views
8

C# ci consente to create custom event accessors.Il campo di supporto di un evento generato dal compilatore garantisce sempre lo stesso nome dell'evento?

Action _custom; 
public event Action Custom 
{ 
    add { _custom = (Action)Delegate.Combine(_custom, value); } 
    remove { _custom = (Action)Delegate.Remove(_custom, value); } 
} 

Se non vengono specificate, the compiler creates them for you. Linguaggio C# spec:

Quando si compila un evento di campo simile, il compilatore crea automaticamente stoccaggio per tenere il delegato, e crea di accesso per l'evento che aggiungere o rimuovere i gestori di eventi al campo delegato.

Il codice sorgente decompilato utilizzando dotPeek per un semplice public event Action Public; si presenta come segue:

private Action Public; 

    public event Action Public 
    { 
    add 
    { 
     Action action = this.Public; 
     Action comparand; 
     do 
     { 
     comparand = action; 
     action = Interlocked.CompareExchange<Action>(
        ref this.Public, comparand + value, comparand); 
     } 
     while (action != comparand); 
    } 
    remove 
    { 
     Action action = this.Public; 
     Action comparand; 
     do 
     { 
     comparand = action; 
     action = Interlocked.CompareExchange<Action>(
        ref this.Public, comparand - value, comparand); 
     } 
     while (action != comparand); 
    } 
    } 

Degno di nota è che il campo e l'evento utilizzano lo stesso nome. Ciò ha portato some people a concludere che è possibile trovare informazioni sul campo di supporto durante la riflessione, cercando il campo nella classe con lo stesso nome dell'evento. Ho implementato in questo modo:

public static FieldInfo GetFieldInfo(this EventInfo eventInfo) 
{ 
    Contract.Requires(eventInfo != null); 

    return eventInfo.DeclaringType.GetField(
     eventInfo.Name, 
     BindingFlags.DeclaredOnly | BindingFlags.Instance | 
      BindingFlags.Public | BindingFlags.NonPublic); 
} 

questo funziona, ma solleva la questione: è il campo appoggio di un evento generato compilatore sempre garantito per utilizzare lo stesso nome come l'evento?

Non è possibile creare accessors di eventi personalizzati che accedono a un delegato con lo stesso nome utilizzando Visual Studio. Questo risulta nel messaggio: "Il membro con lo stesso nome è già dichiarato." Mi chiedo se sia possibile concludere che qualsiasi evento per il quale non è disponibile alcun backing delegato con lo stesso nome è un evento con accessor personalizzati.

risposta

9

Il campo di supporto di un evento generato dal compilatore è sempre garantito per utilizzare lo stesso nome dell'evento?

Jon e Marc hanno completamente ragione di rispondere "No".

Questo è un dettaglio di implementazione non documentato del compilatore, esplicitamente indicato dalla specifica come tale e soggetto a modifiche in qualsiasi momento.

In pratica, è improbabile che ciò cambi. Usiamo il fatto che il campo e l'evento hanno lo stesso nome del modo più semplice per associarli logicamente l'uno con l'altro nel compilatore.

Non è possibile creare accessors di eventi personalizzati che accedono a un delegato con lo stesso nome utilizzando Visual Studio. Questo risulta nel messaggio: "Il membro con lo stesso nome è già stato dichiarato."

Corretto.

Mi chiedo se è possibile concludere che qualsiasi evento per il quale non è disponibile alcun backing delegato con lo stesso nome è un evento con accessori personalizzati.

Non mi sentirei a mio agio a fare una simile conclusione. Potresti essere in grado di raggiungere questa conclusione se sapessi che l'assembly in questione è stato emesso dal compilatore C#. Ma non siamo l'unico gioco in città quando si tratta di emettere assemblaggi. Puoi fare cose piuttosto strane con ILDASM.

Posso chiederti perché vuoi sapere questa roba? Sono d'accordo con Marc; se accedi a un campo tramite Reflection, probabilmente stai sbagliando. Dovresti essere in grado di accedere al campo all'interno della classe senza problemi (perché è solo un campo privato) e al di fuori della classe, non hai alcuna attività per esaminare i dettagli dell'implementazione privata di un'altra classe. È particolarmente importante utilizzare la riflessione per eseguire un controllo di fine corsa sulla sicurezza del filo imposto dagli accessori. Questi accessori sono lì per la tua protezione; non corrergli attorno

+1

_ "Posso chiederti perché vuoi sapere queste cose?" _ 1. Perché è divertente e istruttivo! :) 2. Stavo scrivendo un aspetto usando [PostSharp] (http://www.sharpcrafters.com/) che a un certo punto dovevo conoscere il delegato del backing field. Ho pensato che potrebbe essere un problema ed è stato in grado di utilizzare un altro approccio. –

+0

3. Poiché non ho riscontrato problemi sollevati ad es. [questa risposta] (http://stackoverflow.com/questions/3783267/how-to-get-a-delegate-object-from-an-eventinfo/3783491#3783491) ho pensato che valesse la pena di chiederlo in modo specifico. Sto iniziando a vedere una correlazione tra le mie domande e il terzetto magico che risponde loro. Molte grazie! :) Noterò anche che dovrei davvero iniziare a leggere quella specifica C#. Roba interessante! –

+0

E una divulgazione completa al 100%, sto scrivendo un aspetto che elimina la necessità per me di scrivere _ "delegate {}" _ durante la creazione di eventi, [quindi la mia domanda precedente] (http://stackoverflow.com/q/9823457/590.790). :) Ho appena ottenuto un calcio da questo tipo di cose. Anche se potrebbe essere eccessivo, è un ottimo modo per imparare. :) –

6

No - dalla C# 4 spec (sezione 10.8.1):

All'interno della classe X, i riferimenti a Ev sono compilati per fare riferimento al campo nascosto _ Ev invece. Il nome " _Ev" è arbitrario; il campo nascosto potrebbe avere qualsiasi nome o nessun nome.

Così mentre la compatibilità del codice sorgente è garantita, non c'è garanzia sul nome del campo generato. (In pratica, non mi aspetto che questo cambi presto nel compilatore MS, ma non è garantito, quindi non dovresti fare supposizioni.)

+0

Ok, grazie! Quindi usare 'GetField (eventName)' per trovare il delegato di supporto funzionerebbe solo per gli assembly compilati con il compilatore 'corretto'. Potrebbe essere interessante sapere quanti compilatori usano un nome diverso. –

+1

@StevenJeuris: Non solo: presuppone che l'evento venga implementato con un evento di tipo field in primo luogo. Come dice Marc, se hai bisogno di tornare dietro l'implementazione, stai sbagliando :( –

5

L'implementazione di un evento è un dettaglio di implementazione del compilatore, e differisce tra i compilatori (MS C# 4 ha un'implementazione diversa per MS C# < 4, e anche le specifiche MS ed ECMA non sono d'accordo).

Personalmente, direi: se è necessario accedere al campo di supporto tramite riflessione, probabilmente non si stanno utilizzando gli eventi correttamente.

+0

_ "probabilmente non stai usando correttamente gli eventi" _ ... il mondo degli aspetti è un posto meraviglioso. :) Ma hai ragione, sono già riuscito a scrivere il mio aspetto in modo diverso, eliminando la necessità di ottenere il backing field. Mi stavo chiedendo se dovrei tenere 'GetFieldInfo()' nel mio grimorio, perché mi sembrava pericoloso. È andato ora. ; p –

Problemi correlati