2015-05-26 12 views
6

Ho una classe con proprietà che hanno diversi effetti collaterali che si attivano quando vengono chiamati i suoi setter, come gli eventi di modifica attivati ​​e/o le azioni di rendering. Come potrei progettare una funzione che imposti la proprietà solo se il suo valore deve cambiare?F # - Imposta proprietà se modificato

Con arbitri, vorrei solo fare qualcosa di simile:

let setIfChanged (oldVal: Ref<'T>) (newVal: 'T) = if newVal <> !oldVal then oldVal := newVal 
let y = ref 0 
setIfChanged y 1 

Tuttavia, le proprietà non sono mutabili arbitri, e io non riesco a trovare un modo per progettare una cosa che compilerà. Ad esempio,

let setIfChanged oldVal newVal = if newVal <> oldVal then oldVal <- newVal 

sarà solo mi dicono che OLDVAL non è mutabile.

C'è un modo per annotare oldVal in modo che sia per proprietà modificabili (impostabili)? O c'è un modo migliore?

+0

beh, è ​​un peccato (normalmente vorrai che il setter sia così intelligente da non fare effetti collaterali quando non c'è nulla di cambiato) non puoi cambiare la fonte - personalmente prenderei in considerazione il confezionamento dei tipi * offendenti * - Tomas risposta sembra un bel modo per farlo * abbreviato * – Carsten

risposta

4

si può fare:

let setIfChanged (oldVal: 'a byref) (newVal : 'a) = if newVal <> oldVal then oldVal <- newVal 
let mutable test = 42 
setIfChanged &test 24 

Detto questo, data la tua spiegazione degli obiettivi, mi sento di raccomandare guardando Gjallarhorn. Fornisce supporto per la segnalazione su valori mutabili ed è progettato per essere estremamente flessibile in termini di ciò che accade quando un valore cambia. Potrebbe essere usato come meccanismo sottostante su cui potresti facilmente costruire i tuoi segnali quando le cose cambiano (se non fornisce già ciò che ti serve tramite il modulo View).


Edit:

Dato il commento che la proprietà in questione si trova in un assemblaggio di terze parti, una possibilità sarebbe quella di utilizzare il supporto dinamico in F # per ignorare il op_DynamicAssignment dell'operatore (?<-) per le spedizioni in modo dinamico al metodo, ma alza la tua "notifica" nel processo.

Dato:

let (?<-) source property (value : 'a) = 
    let p = source.GetType().GetProperty(property) 
    let r = p.GetValue(source, null) :?> 'a 
    if (r <> value) then 
     printfn "Changing value" 
     source.GetType().GetProperty(property).SetValue(source, value, null) 

È possibile chiamare someObj?TheProperty <- newVal, e vedrete la stampa "Modifica valore" solo se il valore è effettivamente cambiato.

Internamente, questo funziona utilizzando la riflessione per ottenere il vecchio valore della proprietà e impostare quello nuovo, quindi non avrà le migliori caratteristiche di prestazione, ma come lo si sta usando per aumentare le notifiche di tipo di proprietà modificate, probabilmente non è un problema.

+0

Come funzionerebbe con una proprietà dell'oggetto? ad esempio 'setIfChanged foo.Text" bar "'. Inoltre, non ho il controllo sulle classi e sui loro membri, che sono definiti in una libreria C# esterna. – dannytoone

+1

@dannytoone Ahh, che avrebbe dovuto lavorare contro un campo. Non funzionerà contro una proprietà ... non ti rendi conto che non hai accesso ai tipi. –

+1

@dannytoone Ti ha dato un'alternativa usando il supporto dinamico in F # per "avvolgere" l'accesso, permettendoti di fare le tue operazioni nel processo. –

5

Non penso che ci sia un modo semplice per farlo "gratuitamente".

Un trucco che potrebbe rendere questo un po 'più facile è che si può effettivamente trattare il getter e setter della proprietà come funzioni, in modo da poter scrivere setIfChanged in funzione prendendo due funzioni:

let setIfChanged getter setter v = 
    if getter() <> v then setter(v) 

dato un classe A con proeperty P, quindi è possibile chiamare questo utilizzando set_P e get_P come parametri:

let a = A() 
setIfChanged a.get_P a.set_P 0 
setIfChanged a.get_P a.set_P 1 

il fatto che il compilatore permette di accedere a get_P e set_P sono un trucco poco conosciuto - quindi potrebbe essere un po 'di confusione per molte persone. Per completezza, ecco la definizione di A che ho usato per la prova:

type A() = 
    let mutable p = 0 
    member x.P 
    with get() = p 
    and set(v) = printfn "Setting!"; p <- v 

Un approccio più sofisticato sarebbe quella di utilizzare citazioni e di riflessione, che sarebbe più lento, ma avrebbe lasciato che si scrive qualcosa di simile setIfChanged <@ a.P @> 42.

Problemi correlati