2010-07-12 10 views
6

Quindi devo progettare una classe che lavori su una collezione di oggetti accoppiati. Esiste una mappatura uno-a-uno tra gli oggetti. Mi aspetto che il cliente della classe abbia stabilito questa mappatura prima di usare la mia classe.Qual è il modo più intuitivo per chiedere oggetti a coppie?

La mia domanda è qual è il modo migliore per consentire all'utente della mia classe di fornirmi tali informazioni?

E 'per chiedere una collezione di coppie come questa?

MyClass(IEnumerable<KeyValuePair<Object, Object>> objects) 

Oppure raccolte separate come questa?

MyClass(IEnumberable<Object> x, IEnumerable<Object> y) 

Oppure c'è un'altra opzione?

Mi piace il primo perché la relazione è esplicita, non mi piace per il lavoro extra che mette sul client.

Mi piace il secondo perché i tipi sono più primitivi e richiedono meno lavoro, non mi piace perché la mappatura non è esplicita. Devo presumere che l'ordine sia corretto.

Opinioni per favore?

+0

mi piace la prima per lo stesso motivo, il rapporto è esplicito. Dato che KeyValuePair è comune in tutto il Framework, non vedo alcun "lavoro extra" L'unico svantaggio che potrebbe avere è che KeyValuePair deduce che T2 è in qualche modo codificato da T1, se la relazione tra loro è più semetric, potresti arrotolare il tuo tipo (es. Pair ). – Tergiver

risposta

10

In .NET 4 si dovrebbe utilizzare Tuple<T1,T2>. Ulteriori informazioni sulla classe Tuple su MSDN.

+6

Solo .NET 4: http://msdn.microsoft.com/en-us/library/dd268536.aspx#versionsTitleToggle – Hound

+1

Per il gusto dell'argomento. Perché usare Tuple al posto della tua classe di accoppiamento? –

+0

@korbinian - questo è quello che pensavo, ma MSDN mi ha buttato fuori includendo un collegamento alle "versioni precedenti" che include .NET 3.5. Sfortunatamente, le osservazioni in quel punto (che non ho guardato) indicano sinceramente che si tratta solo di .NET 4. – tvanfosson

1

Il primo è il mio metodo preferito, poiché una sequenza potrebbe essere più lunga dell'altra con la seconda: non mantiene la mappatura 1: 1 richiesta.

1

A mio parere, la seconda opzione offre a te e al cliente più lavoro da fare. La prima opzione è più sicura e più difficile da sbagliare. Sceglierei la prima opzione o qualcosa di simile ogni volta.

+0

Sì, questo è quello che ho detto. –

0

Penso che devi andare con l'opzione 1. Devono esserci relazioni esplicite o stai solo chiedendo dei problemi.

5

Se si ha accesso ad esso, utilizzare Tuple<T, R>. Se non lo fai, scrivi semplicemente una classe generica Tuple o Pair di tua scelta. Eviterei di usare KeyValuePair, solo perché è prolisso e ha un'associazione con Dictionary.

+0

Inoltre, l'ultima volta che ho controllato, KeyValuePair non serializza su XML, quindi questa è un'altra buona ragione per non usarla se è importante per te. – AaronLS

5

Preferirei KeyValuePair dei due che hai menzionato poiché è più espressivo e mantiene la relazione tra gli oggetti.

Ma preferirei creare una classe che contenga un riferimento alla tua coppia. Questo è più leggibile secondo me, e non fa mai male creare una classe o una struttura in più per esprimere le tue azioni reali.

(pseudo-codice)

class MyPair 
{ 
    public TypeA One; 
    public TypeB Two; 
} 

MyClass(IEnumerable<MyPair> objects) 

C'è menzione di Tuple<,> in alcune risposte, che è più leggibili KeyValuePair, ma più flessibile in quanto può contenere più di due parametri.

[Edit - più approfondite su tuple/classi dopo una buona notte di sonno]

To Tuple or Not To Tuple

+0

Perché MyPair dovrebbe implementare IEnumerable? –

+0

@Alex, perché non dovrebbe :) Hai ragione, e apparentemente ho bisogno di dormire. –

+0

Non ti preoccupare per questo motivo - posso riguardare la cosa del sonno :) –

0

Vorrei usare la seconda versione (perché è più semplice) + commenti + controlli statici/dinamici. Se è possibile utilizzare i contratti di codice, provare ad assicurarsi che le raccolte abbiano la stessa lunghezza, non null. In caso contrario, fare lo stesso con Debug.Assert e if ... throw ArgumentException. In alternativa, puoi creare il tuo oggetto contenente la coppia, ma poi diventa più difficile se vuoi usare i generici per i membri della coppia. Inoltre, quando si crea un oggetto che deve essere memorizzato in un contenitore, è necessario implementare correttamente GetHashCode, Equals, ecc. Il primo libro "C# efficace" ha un elemento su questo.

Come regola generale, preferisco non sovrastimolare le firme dei metodi.

0

Di solito fornisco entrambi, con il costruttore KeyValuePair che delega al costruttore di 2-arg. Il meglio di entrambi i mondi.

+0

Solo curioso - Capisco come si può passare dal metodo due arg a quello arg facendo un Zip - come si fa il contrario? –

+0

public MyClass (KeyValuePair pair): this (pair.Key, pair.Value) {} – benjismith

1

Preferirei sicuramente il primo. Come dici tu, la correlazione è esplicita ed è meno soggetta a errori rispetto alla seconda in cui è necessario verificare che entrambe le raccolte abbiano la stessa lunghezza. Naturalmente, potrebbe essere appropriato offrire/entrambi/i modi, a seconda della classe effettiva che stai creando.

Una cosa, se si utilizza .NET 4.0, si consiglia di utilizzare Tuple<T1, T2> anziché KeyValuePair.

0

Mi piace il primo metodo per due motivi.

  1. se stanno memorizzando i loro rapporti in un dizionario <> oggetto già, si può semplicemente mano dal dizionario come è - senza codice aggiuntivo richiesto.

  2. Se si sta utilizzando il proprio un'archiviazione personalizzata allora si dà KeyValuePairs in un IEnumerable è davvero facile con la dichiarazione resa

Qualcosa di simile a questo:

IEnumerable<KeyValuePair<Object,Object>> GetMappedPairs() 
{ 
    foreach(var pair in _myCustomData) 
    { 
     yield return new KeyValuePair{Key = pair.ID, Value = pair.Data}; 
    } 
} 

Opzione numero 2 non è facilmente comprensibile dagli sviluppatori che usano il tuo metodo in modo da richiedere documentazione e commenti per spiegare come usarlo.

Per inciso, si sarebbe probabilmente servito meglio dichiarando il metodo come un generico invece di difficile codifica KeyValuePair

MyClass<TKey,TValue>(IEnumerable<KeyValuePair<TKey,TValue>> pairs); 
0

Può essere opportuno prendere in considerazione il motivo per cui si espone un enumerabile a tutti. Potresti usare un metodo Aggiungi (Oggetto a, Oggetto b) che nasconde completamente il tuo modo interno di trattare le coppie. Il cliente potrebbe quindi impostare i propri mezzi per aggiungere relazioni ad esso come insiemi o singolarmente.

Ovviamente se hai ancora bisogno di un metodo per eseguire un'enumerazione, la prima opzione è probabilmente la migliore: una relazione esplicita. Ma tu consideri l'utilizzo o la creazione di un tipo Tuple o Pair da usare al posto di KeyValuePair, assumendo che la relazione tra loro non sia una delle proprietà Key to Value.

0

E se faceste qualcosa come questo?

// The client can choose to put together an IEnumerable<...> by hand...  
public MyClass(IEnumerable<KeyValuePair<object, object>> pairs) 
{ 
    // actual code that does something with the data pairs 
} 

// OR the client can pass whatever the heck he/she wants, as long as 
// some method for selecting the Xs and Ys from the enumerable data is provided. 
// (Sorry about the code mangling, by the way -- just avoiding overflow.) 
public static MyClass Create<T> 
(IEnumerable<T> source, Func<T, object> xSelector, Func<T, object> ySelector) 
{ 
    var pairs = source 
     .Select(
      val => new KeyValuePair<object, object>(
       xSelector(val), 
       ySelector(val) 
      ) 
     ); 

    return new MyClass(pairs); 
} 

Ciò consentirebbe ai clienti di scrivere codice come questo:

// totally hypothetical example 
var stockReturns = StockReturns.Create(prices, p => p.Close, p => p.PrevClose); 
Problemi correlati