2009-08-06 13 views
10

Sto usando una classe che non posso modificare, ha una proprietà (un booleano) di cui sarebbe bello essere informato quando cambia, non posso modificare le proprietà get o set mentre sto importando la classe da una .dll (che non ho il codice per).Attivare un evento/funzione su una proprietà? (C#)

Come si crea un evento/funzione che viene generato quando la proprietà viene modificata?

Ulteriori
È cambiato solo all'interno della propria classe, direttamente alla variabile privata sottostante.

E.g.

private bool m_MyValue = false; 

public bool MyValue 
    { 
    get { return m_MyValue; } 
    } 

private void SomeFunction() 
    { 
    m_MyValue = true; 
    } 

risposta

10

Non si può, fondamentalmente ... non senza usare qualcosa come l'API del debugger per iniettare il codice al momento dell'esecuzione e modificare l'IL della libreria originale (e non sto raccomandando nessuna di queste soluzioni; altrimenti potrebbe violare la licenza della biblioteca).

Fondamentalmente se una proprietà non supporta la notifica, non supporta la notifica. Dovresti cercare un modo diverso di affrontare il tuo problema. (Per esempio, il polling funziona?)

+0

penso che sto andando ad avere per imboccare la strada di polling, non qualcosa che speravo, perché i dati possono essere reduntant durante tempi sondaggio (I vuoi intercettare i dati prima che vengano utilizzati da altre funzionalità) – ThePower

+0

o avvolgere la classe con la tua classe che supporta INotify o le tue implementazioni? – Pondidum

+0

@Andy: dipende. Se lo fai, non puoi usare la classe come tipo originale, di cui potrebbero aver bisogno altri bit del codice. –

5

Non puoi farlo direttamente [come ha detto Jon Skeet], a meno che non è virtuale, siete in grado di intercettare tutte le creazioni della classe istanza e non ci sono modifiche a un campo di supporto che influenza il vero 'valore' del prezzo.

L'unico modo per forzare la forza bruta è utilizzare Mono.Cecil o MS CCI per strumentare il prop setter a la this DimeCast on Cecil. (O PostSharp)

Tuttavia questo non intrappolerebbe le modifiche interne al campo di supporto (se ce n'è anche uno). (Questo è il motivo per cui il wrapping probabilmente non funzionerà).

AGGIORNAMENTO: Dato l'aggiornamento che si sta tentando di intrappolare la modifica del campo sottostante, l'unico modo per farlo è utilizzare PS/CCI/Cecil e analizzare il flusso per intercettare tutti gli aggiornamenti di campo. In breve, non molto fattibile.

1

Dovrai creare una classe che avvolga la classe nella dll, all'interno della proprietà setter basta generare un evento usando i metodi normali.

+2

Ma questo non intaccherà gli aggiornamenti interni che il componente produce, tramite il suo setter, o le modifiche dirette allo stato interno che il p; ropget potrebbe riflettere. –

-1

Puoi provare ad ereditarlo e usarlo invece che figlio. Sostituisce il "set" della proprietà in modo che sollevi l'evento.

EDIT: ... solo se la proprietà è virtuale nella classe padre ...

+0

solo fattibile è la proprietà è contrassegnata come virtuale ... –

+0

sì ... sono d'accordo ... scusa per l'errore persone ... –

1

Potrebbe ereditare dalla classe e nascondere la proprietà? Qualcosa di simile ...

class MyClass : BaseClass 
{ 
    // hide base property. 
    public new bool MyProperty 
    { 
     get 
     { 
      return base.MyProperty; 
     } 

     set 
     { 
      base.MyProperty = value; 
      RaiseMyPropertyChangedEvent(); 
     } 
    } 
} 
+0

come http://stackoverflow.com/questions/1238137/creating-an- evento-on-a-proprietà-c/1238154 # 1238154. (E l'aggiornamento a Q significa che questo non funzionerà) –

+0

solo fattibile è la proprietà è contrassegnata come virtuale ... –

+1

@Thomas: No, la parola chiave 'nuovo' modificatore non richiede che la base sia virtuale. –

3

Probabilmente, l'unico vero modo per farlo è quello di creare una sorta di componente "watcher", in esecuzione in un thread separato, il cui compito è quello di leggere la proprietà a intervalli ed aumentare un evento quando il valore della proprietà cambia. Naturalmente questa soluzione naviga nelle acque torbide del threading e della sincronizzazione.

Partendo dal presupposto che l'applicazione è a thread singolo rispetto a questo oggetto, la soluzione più pulita è effettuare chiamate di metodo a questo oggetto tramite un oggetto proxy. Avrebbe il compito di controllare lo stato prima e dopo la proprietà e di innalzare un evento nel caso in cui fosse cambiato.

Ecco un semplice esempio di cosa sto parlando:

public class SomeProxy 
{ 
    public SomeProxy(ExternalObject obj) 
    { 
     _obj = obj; 
    } 

    public event EventArgs PropertyChanged; 

    private bool _lastValue; 

    private ExternalObject _obj; 

    protected virtual void OnPropertyChanged() 
    { 
     if(PropertyChanged != null) 
      PropertyChanged(); 
    } 

    protected virtual void PreMethodCall() 
    { 
     _lastValue = _obj.SomeProperty; 
    } 

    protected virtual void PostMethodCall() 
    { 
     if(_lastValue != _obj.SomeProperty) 
      OnPropertyChanged(); 
    } 

    // Proxy method. 
    public SomeMethod(int arg) 
    { 
     PreMethodCall(); 
     _obj.SomeMethod(arg); // Call actual method. 
     PostMethodCall(); 
    } 
} 

Ovviamente si può costruire questo schema delega in un oggetto adatto - devi solo essere consapevoli che tutti chiamate devono passare attraverso il proxy per l'evento da sollevare quando ci si aspetta che sia.

+1

+1 per l'esempio completo (anche se l'OP chiarisce che vuole trattarlo come una scatola nera, quindi la risposta non è appropriata IMNSHO) –

2

Come accennato in precedenza, il metodo più diretto (e quello che richiede la minima modifica al codice) consiste nell'utilizzare una libreria AOP come PostSharp.

Tuttavia, è possibile ottenere una soluzione utilizzando il C# /. NET tradizionale utilizzando lo dependency property pattern, utilizzato attraverso il WPF con grande effetto. Vi suggerisco di leggere su questo, e prendere in considerazione l'implementazione di un tale sistema (o almeno una versione semplificata di esso) per il vostro progetto, se appropriato.

0

Penso idea Alex' di un wrapper è buona, tuttavia, dato che la cosa più importante per voi è che si sa che il valore viene modificato prima dell'uso, si potrebbe spostare semplicemente la notifica al getter, aggirando le preoccupazioni del cambiamento di valore interno. In questo modo si ottiene qualcosa di simile a polling, ma affidabile:

class MyClass : BaseClass 
{ 
    //local value 
    private bool local; 

    //first access yet? 
    private bool accessed = false; 

    // Override base property. 
    public new bool MyProperty 
    { 
     get 
     { 
      if(!accessed) 
      { 
       // modify first-get case according to your taste, e.g. 
       local = base.MyProperty; 
       accessed = true; 
       RaiseMyPropertyChangedBeforeUseEvent(); 
      } 
      else 
      { 
       if(local != base.MyProperty) 
       { 
        local = base.MyProperty; 
        RaiseMyPropertyChangedBeforeUseEvent(); 
       } 
      } 

      return local; 
     } 

     set 
     { 
      base.MyProperty = value; 
     } 
    } 
} 
Problemi correlati