Per come la vedo io, una Tupla è una scorciatoia per scrivere una classe di risultati (sono sicuro che ci sono anche altri usi).
Ci sono davvero altri usi utili perTuple<>
- la maggior parte di essi comporta l'astrazione della semantica di un particolare gruppo di tipi che condividono una struttura simile e il loro trattamento semplicemente come un insieme ordinato di valori. In tutti i casi, un vantaggio delle tuple è che evitano di ingombrare il tuo spazio dei nomi con classi di soli dati che espongono proprietà ma non metodi.
Ecco un esempio di un uso ragionevole per Tuple<>
:
var opponents = new Tuple<Player,Player>( playerBob, playerSam );
Nell'esempio sopra vogliamo rappresentare una coppia di avversari, una tupla è un modo conveniente per accoppiare queste istanze senza dover creare una nuova classe. Ecco un altro esempio:
var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );
Una mano di poker può essere pensata solo come un insieme di carte - e la tupla (può essere) un modo ragionevole di esprimere quel concetto.
mettendo da parte la possibilità che mi manchi il punto di Tuple, l'esempio con una Tupla è una cattiva scelta di design?
Restituire Tuple<>
istanze fortemente tipizzate come parte di un'API pubblica per un tipo pubblico è raramente una buona idea. Come riconosci tu stesso, le tuple richiedono che le parti coinvolte (autore della biblioteca, utente della biblioteca) concordino in anticipo sullo scopo e l'interpretazione dei tipi di tupla utilizzati. È abbastanza impegnativo creare API intuitive e chiare, l'utilizzo Tuple<>
pubblico oscura solo l'intento e il comportamento dell'API.
Anche i tipi anonimi sono una specie di tupla , tuttavia sono fortemente tipizzati e consentono di specificare nomi chiari e informativi per le proprietà appartenenti al tipo. Ma i tipi anonimi sono difficili da usare in diversi metodi: sono stati principalmente aggiunti per supportare tecnologie come LINQ in cui le proiezioni produrrebbero tipi a cui normalmente non vorremmo assegnare nomi. (Sì, so che i tipi anonimi con gli stessi tipi e le proprietà con nome sono consolidati dal compilatore).
La mia regola empirica è: se lo restituirai dalla tua interfaccia pubblica, rendilo un tipo denominato .
Un'altra mia regola empirica per l'uso delle tuple è: argomenti del metodo name e variabili localc di tipo Tuple<>
nel modo più chiaro possibile - fai in modo che il nome rappresenti il significato delle relazioni tra gli elementi della tupla. Pensa al mio var opponents = ...
esempio.
Ecco un esempio di un caso reale in cui ho usato Tuple<>
per evitare di dichiarare un tipo di solo dati da utilizzare solo all'interno del mio assembly . La situazione implica il fatto che quando si utilizzano dizionari generici contenenti tipi anonimi, diventa difficile utilizzare il TryGetValue()
metodo per trovare elementi nel dizionario perché il metodo richiede un out
parametro che non può essere nominato:
public static class DictionaryExt
{
// helper method that allows compiler to provide type inference
// when attempting to locate optionally existent items in a dictionary
public static Tuple<TValue,bool> Find<TKey,TValue>(
this IDictionary<TKey,TValue> dict, TKey keyToFind )
{
TValue foundValue = default(TValue);
bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
return Tuple.Create( foundValue, wasFound );
}
}
public class Program
{
public static void Main()
{
var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
new { LastName = "Sanders", FirstName = "Bob" } };
var peopleDict = people.ToDictionary( d => d.LastName );
// ??? foundItem <= what type would you put here?
// peopleDict.TryGetValue( "Smith", out ??? );
// so instead, we use our Find() extension:
var result = peopleDict.Find( "Smith" );
if( result.First )
{
Console.WriteLine( result.Second );
}
}
}
PS Esiste un altro modo (più semplice) per aggirare i problemi derivanti da tipi anonimi nei dizionari, ovvero utilizzare la var
parola chiave per consentire al compilatore di "dedurre" il tipo per te. Ecco quella versione:
var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
// use foundItem...
}