2010-11-10 14 views
8

Devo passare un evento come parametro a una funzione. C'è un modo per farlo?VB.NET - Passaggio di un evento come parametro

Il motivo è che ho una sequenza di due righe di codice che è disseminata in tutto il mio programma, in cui rimuovo il gestore dinamicamente a un evento, quindi imposto nuovamente il gestore. Lo sto facendo per diversi eventi e gestori di eventi, quindi ho deciso di scrivere una funzione che faccia questo.

Ad esempio, diciamo che ho una casella combinata nel mio codice chiamata combobox1 e ho il gestore chiamato indexChangedHandler. In diversi luoghi del mio codice, ho le seguenti due righe:

RemoveHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler 
AddHandler combobox1.SelectedIndexChanged, AddressOf indexChangedHandler 

Ora, io non voglio continuare a ripetere queste due righe di codice (o simili) in tutto il mio programma, così ho' m alla ricerca di un modo per farlo:

Private Sub setHandler(evt As Event, hndler As eventhandler) 
    RemoveHandler evt, hndler 
    AddHandler evt, hndler 
End Sub 

in modo che tutto il mondo in cui quelle due righe di codice (o simili) si verificano nel mio programma, posso solo sostituirli con:

setHandler(combobox1.SelectedIndexChanged, AddressOf indexChangedHandler) 

Finora , la parte "evt as Event" di a rgument della funzione setHandler sta dando un errore.

P.S: Ho fatto questa domanda su un paio di altri forum e continuo a chiedermi perché vorrei impostare il gestore immediatamente dopo averlo rimosso. Il motivo è perché l'aggiunta dinamica di un gestore di eventi n-times fa sì che il gestore venga eseguito n volte al verificarsi dell'evento. Per evitare ciò, cioè, per garantire che il gestore venga eseguito una sola volta quando si verifica l'evento, prima rimuovo il gestore ogni volta che voglio aggiungere il gestore in modo dinamico.

Si potrebbe chiedere perché il gestore verrà aggiunto più volte in primo luogo ... Il motivo è perché aggiungo il gestore solo dopo un evento particolare, ad esempio E1, nel mio modulo si è verificato (aggiungo il gestore all'interno del gestore dell'evento E1). E l'evento E1 può verificarsi più volte nel mio modulo. Se non rimuovo il gestore ogni volta prima di aggiungerlo di nuovo, il gestore viene aggiunto e quindi eseguito più volte.

In ogni caso, l'elaborazione che si verifica all'interno della funzione non è di estrema importanza per me in questo momento, ma piuttosto il solo mezzo per passare un evento come parametro.

+1

Puoi farci sapere qual è l'errore? –

+0

Non esiste un modo reale di farlo a livello di codice, poiché non è possibile passare i riferimenti a un membro evento. Inoltre, non sono esattamente sicuro di cosa debba fare setHandler ... puoi chiarire il motivo per cui dovresti rimuovere un gestore di eventi e poi aggiungerlo di nuovo? – cdhowie

+0

@cdhowie Ero in procinto di modificare il mio post per spiegare perché avrei dovuto farlo, sono stato interrotto, sono tornato e ho completato il montaggio e l'invio, prima di rendermi conto che hai già fatto la domanda. Per farla breve, la ragione è ora inclusa nel post originale :) – Tracer

risposta

10

Ovviamente è possibile passare eventi in giro ... Beh, è ​​possibile passare Action(Of EventHandler) che può fare quello che vuoi.

Se si dispone di una classe che definisce un evento in questo modo:

Public Class ComboBox 
    Public Event SelectedIndexChanged As EventHandler 
End Class 

Dato un'istanza di ComboBox è possibile quindi creare aggiungere & azioni del gestore di rimuovere in questo modo:

Dim combobox1 = New ComboBox() 

Dim ah As Action(Of EventHandler) 
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h 
Dim rh As Action(Of EventHandler) 
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h 

(Ora, questo è il codice VB.NET 4.0, ma è possibile farlo in 3.5 usando AddressOf e un po 'più di chiacchiere.)

Quindi se ho ve un gestore Foo:

Public Sub Foo(ByVal sender as Object, ByVal e As EventArgs) 
    Console.WriteLine("Over here with Foo!") 
End Sub 

e un metodo Raise su ComboBox ora posso fare questo: "Da questa parte con Pippo"

ah(AddressOf Foo) 
combobox1.Raise() 
rh(AddressOf Foo) 

Questo scrive il messaggio come previsto.

Posso anche creare questo metodo:

Public Sub PassActionOfEventHandler(ByVal h As Action(Of EventHandler)) 
    h(AddressOf Foo) 
End Sub 

posso passare intorno alle azioni del gestore di eventi in questo modo:

PassActionOfEventHandler(ah) 
combobox1.Raise() 
PassActionOfEventHandler(rh) 

che scrive di nuovo il messaggio "Da questa parte con i Foo!".

Ora, un problema che potrebbe essere un problema è che è possibile scambiare accidentalmente l'aggiunta e la rimozione dei delegati del gestore di eventi nel codice, dopo tutto sono dello stesso tipo. Così è facile da definire solo i delegati fortemente tipizzato per l'add e togliere le azioni in questo modo:

Public Delegate Sub AddHandlerDelegate(Of T)(ByVal eh as T) 
Public Delegate Sub RemoveHandlerDelegate(Of T)(ByVal eh as T) 

Il codice per definire le istanze delegate non cambia, tranne per i tipi di delegati:

Dim ah As AddHandlerDelegate(Of EventHandler) 
    = Sub (h) AddHandler combobox1.SelectedIndexChanged, h 
Dim rh As RemoveHandlerDelegate(Of EventHandler) 
    = Sub (h) RemoveHandler combobox1.SelectedIndexChanged, h 

Quindi questo ti consente di essere molto creativo. È possibile definire una funzione che utilizzi il delegato add handler, il delegato del gestore di rimozione e un gestore di eventi, che collegherà un gestore di eventi e quindi restituirà un IDisposable che è possibile utilizzare in un secondo momento per rimuovere il gestore senza la necessità di mantenere un riferimento al gestore eventi. Questo è utile per l'utilizzo delle istruzioni Using.

Ecco la firma:

Function SubscribeToEventHandler(
    ByVal h as EventHandler, 
    ByVal ah As AddHandlerDelegate(Of EventHandler), 
    ByVal rh As RemoveHandlerDelegate(Of EventHandler)) As IDisposable 

Così dato questa funzione ora posso fare questo:

combobox1.Raise() 
Using subscription = SubscribeToEventHandler(AddressOf Foo, ah, rh) 
    combobox1.Raise() 
    combobox1.Raise() 
End Using 
combobox1.Raise() 

E questo scrive il messaggio "Da questa parte con i Foo!" solo due volte Le prime e ultime chiamate Raise sono esterne all'abbonamento al gestore eventi.

Divertiti!

+1

Se ho capito bene, stai passando 'Delegati' in giro, non lo stesso' Eventi', come richiesto nella domanda. Una buona risposta comunque. – Bobby

+0

Enigmatività, grazie per la tua risposta dettagliata. Per me è stato un po 'difficile da capire, dal momento che ho visto alcuni costrutti dall'aspetto strano nel tuo post (la mia prima volta in cui vedo una sintassi del genere, quindi ti prego di scoprire con me). Ma da quello che ho capito, devo sempre prima definire esplicitamente l'oggetto e l'evento in "ah" e "rh" giusto? quello che mi piacerebbe sapere è, comunque, non limitare la definizione di "ah" a "combobox1.selectedIndexChanged", in modo che io possa usare la stessa funzione per impostare il gestore su un oggetto diverso (cbx2), ad esempio, " cbx2.selectedIndexChanged". – Tracer

+0

@ 'Tracer' - Sì. Basta cambiare le definizioni dei delegati in 'Delegato pubblico Sub AddHandlerDelegate (Of T) (ByVal eh come T, comboBox come ComboBox)' e puoi farlo funzionare per qualsiasi 'ComboBox'. se devi fare altri eventi ti suggerisco di chiamare il delegato 'AddSelectedIndexChangedHandler' ecc. Un add e remove delegate per ogni evento e controllo. – Enigmativity

2

Non è possibile passare eventi in giro. Event non è un tipo, è una parola chiave che definisce una coppia di metodi, aggiungi e rimuovi, utilizzata per modificare lo stato del membro evento della classe. Sono molto simili alle proprietà in questo senso (che hanno i metodi get e set).

Come le proprietà, i metodi di aggiunta e rimozione possono fare tutto ciò che si desidera. In genere, questi non faranno altro che mantenere un'istanza delegata, che a sua volta è una MulticastDelegate o, in altre parole, un elenco di delegati che vengono richiamati uno alla volta se l'evento viene generato.

È possibile visualizzare chiaramente questa struttura in C#, dove non è mascherata con AddHandler/RemoveHandler, ma si modifica direttamente l'elenco dei gestori associati: myObject.Event += new Delegate(...);.

E, ancora come proprietà, essendo un membro di una classe che in realtà astrae due metodi diversi, non è possibile passare letteralmente un evento come oggetto.

+0

Grazie per questa spiegazione, Bobby. – Tracer

2

Ci sono diversi significati per il termine "evento", a seconda del contesto. Nel contesto che stai cercando, un evento è una coppia di metodi che aggiungerà o rimuoverà in modo efficace un delegato da un elenco di iscrizioni. Sfortunatamente, in VB.NET non esiste una classe che rappresenti un indirizzo di metodo senza un oggetto collegato.Probabilmente si potrebbe usare reflection per recuperare i metodi appropriati e quindi passare quei metodi a una routine che li chiamerebbe entrambi, ma nella tua particolare situazione che probabilmente sarebbe stupida.

L'unica situazione in cui posso vedere che potrebbe essere utile passare un evento nel senso che descrivi sarebbe qualcosa di simile a un oggetto IDisposable che sottoscrive eventi di oggetti più longevi e che deve annullare l'iscrizione a tutti i suoi eventi quando è disposto. In tal caso, può essere utile utilizzare reflection per recuperare il metodo remove-delegate per ciascun evento e quindi chiamare tutti i metodi remove-delegate quando un oggetto viene eliminato. Sfortunatamente, non conosco alcun modo per rappresentare gli eventi da recuperare tranne come stringhe letterali, la cui validità non può essere verificata fino all'ora di esecuzione.

Problemi correlati