2010-07-03 17 views
9

Sto scrivendo un bijective dictionary class, ma voglio assicurarmi che i due tipi generici non siano dello stesso tipo per due motivi.Utilizzo di dove specificare diversi generici

In primo luogo, vorrei che per implementare l'interfaccia IDictionary in entrambe le direzioni, ma

public class BijectiveDictionary<TKey, TValue> 
    : IDictionary<TKey, TValue>, IDictionary<TValue, TKey> 

mi dà " 'BijectiveDictionary < TKey, TValue >' non può implementare sia 'IDictionary < TKey, TValue >' e 'IDictionary < TValue, TKey >' perché possono unificare per alcune sostituzioni dei parametri di tipo "(che è comprensibile, ma non desiderabile.)

In secondo luogo, vorrei scrivere un opt soluzione imitata se entrambi i tipi sono uguali.

public class BijectiveDictionary<TKey, TValue> 
    : IDictionary<TKey, TValue> where TValue : TKey 
{ 
    // Optimized solution 
} 

public class BijectiveDictionary<TKey, TValue> 
    : IDictionary<TKey, TValue>, IDictionary<TValue, TKey> where TValue : !TKey 
{ 
    // Standard solution 
} 

È possibile?

In caso contrario, posso considerare la mancata attuazione IDictionary, ma non ho potuto garantire TValue this[TKey key] e TKey this[TValue key] sarebbero diverse, il che sarebbe un peccato.


Sembra che il problema qui sia che quando i due tipi sono uguali, si presentano i casi speciali.

Il mio intento originale era quello di creare un dizionario che mappa esattamente una chiave per esattamente un valore, e viceversa, tale che per ogni KeyValuePair<TKey, TValue>(X, Y), un KeyValuePair<TValue, TKey>(Y, X) esiste pure.

Quando TKey = TValue, allora questo può essere semplificata fino a un singolo dizionario:

public T this[T key] 
{ 
    get { return this[key]; } 
    set 
    { 
     base.Add(key, value); 
     base.Add(value, key); 
    } 
} 

In questo caso, non è possibile Add(2,3); Add(3,4) perché Add(2,3) mappe 3-2 pure, e [3] sarebbe tornato 2.

Tuttavia, Jaroslav Jandek's solution proposto utilizzando un secondo dizionario per fare ciò per i casi in cui TKey! = TValue. E anche se questo funziona meravigliosamente per questi casi, alla fine non segue il mio intento originario quando TKey = TValue, consentendo a Add(2,3); Add(3,4) di mappare una singola chiave 3 a due valori (2 in una direzione, e 4 nell'altra), anche se credo che in senso stretto sia ancora una funzione biettiva valida.

+0

Che cosa stai usando il dizionario per? Hai anche bisogno della parte di suriezione del tuo dizionario? Detto questo, il codice nella mia risposta gestisce tutti i tuoi casi (implementando IDictonary, fornendo bijection e gestendo casi in cui TKey == TValue). –

+2

Se si desidera la bijection per 'TKey == TValue', è possibile utilizzare' SelfBijectiveDictionary : BijectiveDictionary {...} 'e sovrascrivere il suo metodo' Add' e controllare prima 'if (base.Contains (chiave) | | this.Reversed.Contains (value)) throw ... '. Le classi dovrebbero essere nominate diversamente in ogni caso, perché una è bijective 'X-> Y' e l'altra' S-> S' (biiettiva a se stessa). –

+0

Indicare la differenza tra 'X-> Y' e' S-> S' aiuta molto. Ci scusiamo per tutta la confusione! E sì - sto facendo due dizionari separati per loro. Grazie per l'aiuto. – dlras2

risposta

3

ne dite di questo (approccio diverso):

public class BijectiveDictionary<TKey, TValue> : Dictionary<TKey, TValue> 
{ 
    public BijectiveDictionary<TValue, TKey> Reversed { get; protected set; } 

    public BijectiveDictionary() 
    { 
     this.Reversed = new BijectiveDictionary<TValue,TKey>(true); 
     this.Reversed.Reversed = this; 
    } 

    protected BijectiveDictionary(bool reversedWillBeSetFromTheCallingBiji) { } 

    protected void AddRaw(TKey key, TValue value) 
    { 
     base.Add(key, value); 
    } 

    // Just for demonstration - you should implement the IDictionary interface instead. 
    public new void Add(TKey key, TValue value) 
    { 
     base.Add(key, value); 
     this.Reversed.AddRaw(value, key); 
    } 

    public static explicit operator BijectiveDictionary<TValue, TKey>(BijectiveDictionary<TKey, TValue> biji) 
    { 
     return biji.Reversed; 
    } 
} 

e nel codice:

BijectiveDictionary<int, bool> a = new BijectiveDictionary<int, bool>(); 

a.Add(5, true); 
a.Add(6, false); 

Console.WriteLine(a[5]);// => True 
Console.WriteLine(((BijectiveDictionary < bool, int>)a)[true]);// => 5 
//or 
Console.WriteLine(a.Reversed[true]);// => 5 
+1

Nota: il cast esplicito 'Console.WriteLine (((BijectiveDictionary < int, int>) a) [vero]);' ** ** non funziona quando i tipi di set sono gli stessi. È per ovvi motivi: volevo segnalarlo comunque. –

2

In breve, no. Per ottimizzarlo, un'opzione potrebbe essere una strategia basata su un inizializzatore statico, che sceglie un'implementazione concreta appropriata per il caso dello stesso tipo, ma in caso contrario; vale a dire

public class BijectiveDictionary<TKey, TValue> 
    : IDictionary<TKey, TValue> 
{ 
    static readonly ISomeStrategy strategy; 
    static BijectiveDictionary() { 
     if(typeof(TKey) == typeof(TValue)) { 
      strategy = ... 
     } else { 
      strategy = ... 
     } 
    } 
} 

ma sarà quasi certamente trovare ad usare MakeGenericType e di riflessione per creare l'istanza (ma una volta creato dovrebbe andare bene - e farlo solo una volta).

-1

Anche se si potesse, quale sarebbe il risultato di questo codice?

var dict = new BijectiveDictionary<int, int>(); 

dict.Add(2,3); 
dict.Add(3,4); 

Console.WriteLine(dict[3]); // Do I get 2 or 4? 

Forse è possibile ristrutturare il codice in un altro modo che non è ambiguo?

+0

Non potevi farlo, perché ciò avrebbe rotto la natura biiettiva del dizionario (a meno che non fraintendessi la biiezione, che è possibile.) 'Dict.Add (3,4);' errerebbe perché 3 è già mappato a 2. – dlras2

+0

@ Vilx-: Esattamente, questo è il motivo per cui ho usato un altro approccio. Con il mio esempio, si otterrebbe implicitamente ed esplicitamente 4 2. –

+0

@ Daniel Rasmussen: 3 non è mappata a 2. 3 è mappata a 4. Dovete capire che '3 dal set A' è diverso da' 3 dal set B '. My ex differenzia specificamente tra i 2 set (rilevabile da un puntatore di riferimento). –

2

In una certa misura, questo può essere fatto! Io uso un metodo di differenziazione, invece di qualificatore (s) che limita i tipi.

Non unificare, in realtà potrebbe essere migliore di se lo ha fatto perché si può prendere in giro le interfacce separate a parte.

Vedi il mio post qui, con un esempio completamente funzionante in un altro contesto. https://stackoverflow.com/a/12361409/471129

In sostanza, quello che fai è aggiungere un altro parametro di tipo a IIndexer, in modo che diventa IIndexer <TKey, TValue, TDifferentiator>.

Poi quando lo si utilizza due volte, si passa "Prima" al primo utilizzo, e "Seconda" per il 2 ° utilizzo

Quindi, classe Test diventa: class Test<TKey, TValue> : IIndexer<TKey, TValue, First>, IIndexer<TValue, TKey, Second>

Così, si può fare new Test<int,int>()

dove in primo luogo, e la seconda sono banali:

interface First { } 

interface Second { } 
+0

Sembra hackish, ma funziona. +1 –

Problemi correlati