2009-03-13 3 views
5

Ho un po 'di problemi con una classe wrapper un po' semplice che ho.C# imita l'overrideing dell'operatore di assegnazione (=)

Sembra qualcosa di simile a questo:

public class Wrapper<T> 
{ 
    private T _value; 

    public Wrapper<T>(T value) 
    { 
    _value = value; 
    } 

    public static implicit operator Wrapper<T>(T value) 
    { 
    return new Wrapper<T>(value); 
    } 

    public static implicit operator T(Wrapper<T> value) 
    { 
    return value._value; 
    } 
} 

ho sovrascritto i convertitori impliciti da e verso T, in modo che si comporta quasi come l'istanza di T stessa.

ad es.

Wrapper<int> foo = 42; 

Comunque io ho un piccolo problema durante l'assegnazione di un'istanza di Wrapper ad un altro, dal momento voglio solo assegnare il valore della seconda classe wrapper.

Così adesso, devo fare questo:

Wrapper<int> foo = 42; 
Wrapper<int> bar = (int)foo; 

o esporre _value pubblicamente attraverso una proprietà.

Tuttavia, poiché questo è in una libreria e non voglio che l'utente dipenda dal ricordare questo, avete qualche idea su come potrei imitare l'overriding dell'operatore di assegnazione?

Il problema con la semplice modifica del puntatore (come quando si assegna un'istanza di classe a un altro), è che ho un dizionario di puntatori a questi oggetti Wrapper, quindi non posso cambiarli tutto il tempo, il dizionario smetterebbe di corrispondere quindi.

posso vedere se questo è un po 'confusa, quindi se ho lasciato qualcosa di importante fuori, non esitate a chiedere :-)

+0

è stato mai risolto? Ho un problema simile - tranne che sto creando qualcosa per avvolgere i tipi di base, ad es. fluttuare e voler essere in grado di eseguire MyType < float > a = 1f; a = 2f; senza creare una nuova istanza ... i riferimenti vengono davvero aggiornati con la magia? Vorrei solo provare ma non sono convinto che sia un buon esperimento perché se funziona potrebbe essere cieca fortuna per tutto quello che so ... – jheriko

risposta

1

Se si guarda a Nullable <T> ... che fa un cosa simile a quello che stai facendo qui, espone il valore interno usando una proprietà .Value.

Il problema solo cambiando il puntatore (come avviene quando l'assegnazione di un'istanza di classe ad un altro), è che ho un dizionario di puntatori a questi oggetti Wrapper, quindi non posso averli cambiando tutto il tempo , dal momento che il dizionario smetterebbe di corrispondere quindi.

Non sono sicuro di seguirlo, che cosa stai memorizzando esattamente nel dizionario? Perché se si memorizzano i riferimenti, il CLR li aggiornerà se necessario.

+1

Come ho capito, Nullable è stato fornito un supporto aggiuntivo per il compilatore per coprire questo tipo di incarichi scenari. – ajlane

3

È possibile creare Wrapper <T> a struct. Tuttavia non sono sicuro che questo si adatti al design dell'applicazione o no.

5

Poiché l'operatore di assegnazione non può essere sovraccaricato, non esiste una soluzione reale. Come ha fatto notare qualcun altro, l'uso di una struttura ti darà la semantica di assegnazione che desideri, ma poi ti troverai di fronte a semantica del valore - spesso non è una buona cosa.

Una possibilità è quella di sovraccaricare il costruttore:

public Wrapper(Wrapper<T> w) 
{ 
    _value = w._value; 
} 

che si tradurrebbe in questa sintassi:

Wrapper<int> foo = 42; 
Wrapper<int> bar = new Wrapper<int>(foo); 

Anche se più dettagliato di quello che hai, si legge meglio.

Oppure si potrebbe aggiungere un metodo Clone (non l'interfaccia ICloneable), in modo che si potrebbe scrivere:

Wrapper<int> bar = foo.Clone(); 

Si potrebbe ottenere veramente creativo e sovraccaricare qualche operatore, rendendo fare sostanzialmente nulla. Non lo consiglierei, comunque. L'uso dell'overloading dell'operatore per questo genere di cose rende in genere il codice criptico e spesso si interrompe.

0

Questo è il tipo di proprietà. Ti permettono di definire cosa significa assegnare. Non è possibile definirlo per una classe o una struttura perché sono già definiti dalla lingua per fare le cose necessarie. Basta aggiungere una proprietà Value alla classe.

In alternativa, modifica la tua domanda per fornire una descrizione più ampia del tuo progetto e come si integra questo Wrapper, in quanto qualcuno potrebbe suggerire un approccio più semplice.

0

Ho appena esaminato, rendendo la classe una struct non è davvero un'opzione, poiché ha una certa logica nel costruttore senza parametri, in più eredita una classe astratta, che contiene funzioni astratte interne.

Non riesco a utilizzare un'interfaccia, in quanto renderebbe pubbliche tali funzioni, il che interromperà completamente la logica.

Posso postare l'intera classe se fosse utile, ma è un po 'lungo (130 righe) Oppure potrei saltare su un server separato, se fosse meglio? (Se fa male l'integrità di questa domanda, come posso eliminarlo alla fine da tale server)

che spiega anche la classe è davvero difficile, senza scrivere un saggio completo: -/

Comunque cercherò di illustrare il problema che sto avendo.

assumere 2 classi della tabella: CustomerTable e usertable:

public class CustomerTable 
{ 
    Wrapper<string> Name; 
} 

public class UserTable 
{ 
    Wrapper<string> Name; 
} 

Ora il problema è che qualche altro sviluppatore, può utilizzare il codice di cui sopra come segue:

CustomerTable customer = new CustomerTable(); 
UserTable user = new UserTable(); 
user.Name = customer.Name; // This breaks my internal dictionary 

quello che lo sviluppatore dovrebbe avevano fatto , per funzionare, era:

user.Name = (string)customer.Name; 

Il problema è comunque, chi nella loro mente penserebbe a questo, quando si scrive codice?

Anche se ho usato un proprietà Value, lo sviluppatore avrebbe ancora ricordarsi di scrivere

user.Name = customer.Name.Value; // or user.Name.Value = .... 

E ancora lo sviluppatore può dimenticare questo, e tutto ad un tratto egli ottiene eccezioni, o peggio: i dati che non è persistente nel database.

Quindi il mio problema è in realtà, che voglio che il wrapper sia completamente trasparente (dovrebbe essere utilizzabile come se fosse in realtà la classe/primitiva che sta eseguendo il wrapping). Tuttavia, quando si assegna da un wrapper a un altro, la mia logica interna si interrompe.

Un sacco di scrittura e un sacco di codice - fammi sapere se esagero con la scrittura.

+2

Vorrei usare la proprietà Value. Il casting implicito di un tipo in entrambi i modi non è mai una buona idea. Gli sviluppatori down-stream impareranno molto rapidamente come utilizzare correttamente la libreria quando le loro ingenue assegnazioni non riescono a compilare. – ajlane

0

Non lanciare implicitamente il proprio wrapper in entrambi i modi.

public class DBValue<T> 
{ 
    public static implicit operator DBValue <T>(T value) 
    { 
     return new DBValue<T>(value); 
    } 

    public static explicit operator T(DBValue <T> dbValue) 
    { 
     return dbValue.Value; 
    } 

    private readonly T _value; 
    public T Value { get { this._value; } } 

    public DBValue(T value) 
    { 
     this._value = value; 
    } 
} 

Casting da DBValue<T> a T è una conversione con perdita di dati (come minimo, si perde il fatto che si tratta di un valore dal database), e best-practice dovrebbe essere esplicito. Se non perdi nulla eseguendo il casting da DBValue<T> a T, potresti anche utilizzare le proprietà che restituiscono T.

Fondamentalmente, hai già visto perché non dovresti provare a farlo: se un DBValue può essere sostituito da T e viceversa, come fa il compilatore (o lo sviluppatore) a sapere quale scegliere?

richiedono gli sviluppatori a valle a scrivere:

string value = MyProperty.Value 

o

string value = (string)MyProperty 

invece di

string value = MyProperty 

... non è poi così oneroso, e fa in modo che tutti sanno esattamente cosa sta succedendo.

EDIT:

Per rispondere alla domanda in realtà, non si può ignorare l'assegnazione di riferimento - o far sembrare che hai - ma non si dovrebbe davvero bisogno di.

+0

Dovrei notare che in realtà ho commesso questo errore un paio di volte - in particolare mi ha causato problemi con i numeri: cioè qual è il tipo di 1 + myValue se myValue è una classe che funziona come double? – ajlane

0

A J Lane Capisco cosa intendi, e penso che tu abbia ragione - volevo solo rendere il più semplice possibile utilizzare la libreria.

Il motivo per il cast implicito da DbValue a T, è semplicemente alle funzioni che pretende T.

per esempio

literalSomething.Text = Server.HtmlEncode(SomeTable.SomeStringColumn); 

anziché

literalSomething.Text = Server.HtmlEncode((string)SomeTable.SomeStringColumn); 

Ciò richiede il cast essere implicito

Detto questo, leggo il tuo commento mentre digito questo, e posso vedere che è proprio questo il problema.

Penso che tornerò a esporre il valore attraverso una proprietà, richiede solo che lo sviluppatore digiti di più, e in qualche modo rende il codice brutto penso.

Provate a immaginare DbValue:

if (someValue.Value.HasValue) // someValue is DbValue<int?> 

Ma poi di nuovo è probabilmente meglio con il codice di "brutto", rispetto al codice che si comporta in modo diverso da quello che ci si aspetta semplicemente leggendolo.

Immagino che questa domanda sia davvero una "best practice".

Quindi, per concludere, creerò una proprietà Value e la userò al posto dei cast impliciti, e lo sviluppatore che usa la libreria dovrà solo conviverci.

Grazie per il vostro input :-)

+0

Per un codice più leggibile (meno "brutto"), puoi sempre assegnare int? value = someValue.Value; e quindi procedere come previsto. – ajlane

0

Questo vecchi alambicchi pubblicare bisogno di informazioni supplementari per essere completa. È evidente che il comportamento originale desiderato non può essere realizzato poiché l'operatore = non può essere sovraccaricato, e allo stesso modo C# non può essere "ingannato" nel lanciare un oggetto al proprio tipo ... si ridurrà sempre a un assegnamento di riferimento di classe. Ma gli ulteriori post di Steffen mostrano che la classe Wrapper viene utilizzata non solo con le variabili locali, ma come un tipo di campo . È possibile utilizzare la semantica desiderata E mantenere l'integrità del dizionario interno utilizzando le proprietà di classe anziché i campi pubblici.


Anche mantenendo l'originale dato Wrapper<T> classe con entrambi i suoi gestori impliciti, ecco il codice che avrebbe funzionato:

public class CustomerTable 
{ 
    private Wrapper<string> _Name; 
    public Wrapper<string> Name { 
     get { return _Name; } 
     set { _Name = (string)value; } 
    } 
} 

public class UserTable 
{ 
    private Wrapper<string> _Name; 
    public Wrapper<string> Name { 
     get { return _Name; } 
     set { _Name = (string)value; } 
    } 
} 

Se questo cambiamento sono stati fatti, non sarebbe rompere il codice esistente in quanto permette ancora varie modalità di impostazione della proprietà:

CustomerTable customer = new CustomerTable(); 
UserTable user = new UserTable(); 

user.Name = customer.Name; //*** No longer breaks internal data structures 

user.Name = "string literal"; // Works as expected with implicit cast operator 
user.Name = (string)customer.Name; // Still allowed with explicit/implicit cast operator 
user.Name = customer.Name.Value; // Also works if Value property is still defined 

Perché questo non risponde alla domanda originale, utilizzo del wrapper la classe potrebbe ancora essere problematica se usata al di fuori del contesto di proprietà della classe, cioè tra oggetto, ecc. Forse l'intera classe Wrapper potrebbe essere eliminata con la corretta progettazione della classe, compreso l'uso di proprietà set/get accessors.