Як я це бачу, Tuple - це ярлик для написання результатного класу (я впевнений, що є й інші способи використання).
Дійсно, є й інші цінні цілі використанняTuple<>
- більшість із них передбачає абстрагування семантики певної групи типів, що поділяють подібну структуру, і трактує їх просто як упорядкований набір значень. У всіх випадках користь кортежів полягає в тому, що вони уникають захаращувати ваш простір імен класами лише для даних, які розкривають властивості, але не методи.
Ось приклад розумного використання для Tuple<>
:
var opponents = new Tuple<Player,Player>( playerBob, playerSam );
У наведеному вище прикладі ми хочемо представити пару опонентів, кортеж - це зручний спосіб сполучення цих екземплярів без необхідності створення нового класу. Ось ще один приклад:
var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );
Рука покеру може розглядатися як лише набір карт - і набирати (може бути) розумний спосіб вираження цього поняття.
відміняючи можливість того, що я пропускаю точку Tuples, чи є приклад із Tuple поганим вибором дизайну?
Повернення сильно набраних Tuple<>
примірників як частини публічного API для публічного типу рідко є доброю ідеєю. Як ви самі визнаєте, кортежі вимагають, щоб залучені сторони (автор бібліотеки, користувач бібліотеки) заздалегідь домовились про мету та тлумачення типів кортежу, що використовується. Досить складно створити інтуїтивно зрозумілі та зрозумілі API, використовуючи Tuple<>
лише загальнодоступні затінення намірів та поведінки API.
Анонімні типи також є різновидом кортежу - однак вони сильно набрані та дозволяють вказати чіткі, інформативні назви властивостей, що належать до типу. Але анонімні типи важко використовувати в різних методах - вони в основному додавались до таких технологій, як LINQ, де проекції створювали б типи, яким ми зазвичай не хотіли б присвоювати імена. (Так, я знаю, що анонімні типи з однаковими типами та названими властивостями консолідуються компілятором).
Моє правило: якщо ви повернете його з вашого загальнодоступного інтерфейсу - зробіть його іменованим типом .
Інше моє правило щодо використання кортежів полягає в тому, щоб: назви аргументів методу імен і локальних змінних типу було Tuple<>
максимально чітко - змусити ім'я представляти значення взаємозв'язків між елементами кортежу. Подумайте на моєму var opponents = ...
прикладі.
Ось приклад випадку в реальному світі, коли я Tuple<>
уникав оголошення типу лише для даних для використання лише в межах своєї власної збірки . Ситуація пов'язана з тим, що при використанні загальних словників, що містять анонімні типи, важко використовувати TryGetValue()
метод пошуку елементів у словнику, оскільки для цього потрібен out
параметр, який не можна назвати:
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 Існує ще один (простіший) спосіб вирішити проблеми, що виникають з анонімних типів у словниках, і це використовувати var
ключове слово, щоб компілятор "міг" визначити тип для вас. Ось ця версія:
var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
// use foundItem...
}