it-swarm.com.ru

Практический пример, где Tuple можно использовать в .Net 4.0?

Я видел Tuple, представленный в .Net 4, но я не могу представить, где его можно использовать. Мы всегда можем сделать пользовательский класс или структуру.

94
Amitabh

В том-то и дело - удобнее нет постоянно создавать собственный класс или структуру. Это улучшение, такое как Action или Func... Вы можете создавать эти типы самостоятельно, но удобно, что они существуют в фреймворке.

82
tanascius

С помощью кортежей вы можете легко реализовать двумерный словарь (или n-мерный в этом отношении). Например, вы можете использовать такой словарь для реализации сопоставления обмена валют:

var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);

decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
73
MarioVW

В журнале MSDN есть отличная статья , в котором рассказывается о проблемах с животом и дизайне, которые привели к добавлению Tuple в BCL. Выбор между типом значения и ссылочным типом особенно интересен.

Как ясно из статьи, движущей силой Tuple было то, что многие группы внутри Microsoft использовали это, F # команда впереди. Хотя это и не упомянуто, я считаю, что новое ключевое слово «dynamic» в C # (и VB.NET) также имеет к этому какое-то отношение, кортежи очень распространены в динамических языках.

В остальном он не особенно хорош для создания собственного poco, по крайней мере, вы можете дать участникам более подходящее имя.


ОБНОВЛЕНИЕ: из-за большой ревизии в C # версии 7, теперь стало намного больше любви к синтаксису. Предварительное объявление в это сообщение в блоге .

25
Hans Passant

Я использовал Tuple для решения Задача 11 Project Euler

class Grid
{
    public static int[,] Cells = { { 08, 02, 22, // whole grid omitted

    public static IEnumerable<Tuple<int, int, int, int>> ToList()
    {
        // code converts grid to enumeration every possible set of 4 per rules
        // code omitted
    }
}

Теперь я могу решить всю проблему с помощью:

class Program
{
    static void Main(string[] args)
    {
        int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
        Console.WriteLine("Maximum product is {0}", product);
    }
}

Я мог бы использовать для этого собственный тип, но он бы выглядел точно так же, как Tuple.

23
Craig Stuntz

Вот небольшой пример - скажем, у вас есть метод, который должен искать дескриптор пользователя и адрес электронной почты, учитывая идентификатор пользователя. Вы всегда можете создать собственный класс, который содержит эти данные, или использовать параметр ref/out для этих данных, или вы можете просто вернуть Tuple и иметь сигнатуру метода Nice без необходимости создания нового POCO. 

public static void Main(string[] args)
{
    int userId = 0;
    Tuple<string, string> userData = GetUserData(userId);
}

public static Tuple<string, string> GetUserData(int userId)
{
    return new Tuple<string, string>("Hello", "World");
}
22
Tejs

Синтаксис Tuple в C # смехотворно громоздкий, поэтому объявлять кортежи больно. И у него нет сопоставления с образцом, поэтому их также сложно использовать.

Но иногда вам просто нужна специальная группировка объектов без создания для нее класса. Например, скажем, я хотел объединить список, но я хотел два значения вместо одного:

// sum and sum of squares at the same time
var x =
    Enumerable.Range(1, 100)
    .Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));

Вместо того чтобы объединять коллекцию значений в один результат, давайте расширим один результат в коллекцию значений. Самый простой способ написать эту функцию:

static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
    Tuple<T, State> res;
    while ((res = f(seed)) != null)
    {
        yield return res.Item1;
        seed = res.Item2;
    }
}

f преобразует некоторое состояние в кортеж. Мы возвращаем первое значение из кортежа и устанавливаем наше новое состояние на второе значение. Это позволяет нам сохранять состояние на протяжении всего вычисления.

Вы используете его как таковой:

// return 0, 2, 3, 6, 8
var evens =
    Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
    .ToList();

// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
    Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
    .Take(10).ToList();

evens довольно прост, но fibs немного умнее. Его state на самом деле является Tuple, который содержит fib (n-2) и fib (n-1) соответственно.

16
Juliet

Мне не нравится злоупотреблять ими, так как они создают код, который не объясняет сам себя, но они прекрасно реализуют составные ключи на лету, поскольку они реализуют IStructuralEquatable и IStructuralComparable (для использования как для поиска, так и для упорядочения цели). 

И они объединяют все хеш-коды своих предметов, внутренне; например, вот код GetHashCode (взятый из ILSpy):

    int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
    {
        return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
    }
7
Notoriousxl

Кортежи отлично подходят для одновременного выполнения нескольких асинхронных операций IO и возврата всех значений вместе. Вот примеры того, как это делается с Tuple и без него. Кортежи могут сделать ваш код более понятным!

Без (грязное вложение!)

Task.Factory.StartNew(() => data.RetrieveServerNames())
    .ContinueWith(antecedent1 =>
        {
            if (!antecedent1.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
                Task.Factory.StartNew(() => data.RetrieveLogNames())
                    .ContinueWith(antecedent2 =>
                        {
                            if (antecedent2.IsFaulted)
                            {
                                LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
                                Task.Factory.StartNew(() => data.RetrieveEntryTypes())
                                    .ContinueWith(antecedent3 =>
                                        {
                                            if (!antecedent3.IsFaulted)
                                            {
                                                EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
                                            }
                                        });
                            }
                        });
            }
        });

С кортежем

Task.Factory.StartNew(() =>
    {
        List<string> serverNames = data.RetrieveServerNames();
        List<string> logNames = data.RetrieveLogNames();
        List<string> entryTypes = data.RetrieveEntryTypes();
        return Tuple.Create(serverNames, logNames, entryTypes);
    }).ContinueWith(antecedent =>
        {
            if (!antecedent.IsFaulted)
            {
                ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
                LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
                EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
            }
        });

Если вы использовали анонимную функцию с подразумеваемым типом в любом случае , то вы не делаете код менее понятным с помощью кортежа. Восстанавливать кортеж из метода? Используйте экономно, когда ясность кода является ключевой, по моему скромному мнению. Я знаю, что функциональному программированию на C # трудно сопротивляться, но мы должны рассмотреть все эти старые неуклюжие «объектно-ориентированные» программисты C #.

6
AndyClaw

Я склонен избегать Tuple для большинства сценариев, поскольку это ухудшает читабельность. Тем не менее, Tuple полезен, когда вам нужно сгруппировать несвязанные данные.

Например, предположим, у вас есть список автомобилей и городов, в которых они были приобретены:

Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle

Вы хотите объединить количество автомобилей для каждого города:

Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]

Для этого вы создаете Dictionary. У вас есть несколько вариантов:

  1. Создайте Dictionary<string, Dictionary<string, int>>.
  2. Создайте Dictionary<CarAndCity, int>.
  3. Создайте Dictionary<Tuple<string, string>, int>.

Читаемость теряется при первом варианте. Вам потребуется написать гораздо больше кода.

Второй вариант работает и лаконичен, но автомобиль и город на самом деле не связаны и, вероятно, не принадлежат к одному классу.

Третий вариант - лаконичен и чист. Это хорошее использование Tuple.

5
John Kurlak

Кортежи широко используются в функциональных языках, которые могут с ними делать больше, теперь F # является «официальным» языком .net, с которым вы можете взаимодействовать из C # и передавать их между кодом, написанным на двух языках.

5
Mant101

Несколько примеров из головы:

  • Расположение X и Y (и Z, если хотите)
  • ширина и Высота
  • Все измерено с течением времени

Например, вы не захотите включать System.Drawing в веб-приложение только для того, чтобы использовать Point/PointF и Size/SizeF.

4
James Westgate

Вы должны быть очень осторожны с использованием Tuple и, возможно, дважды подумать, прежде чем сделать это. Из моего предыдущего опыта я узнал, что использование Tuple делает код очень трудным для чтения и поддержки в будущем. Некоторое время назад мне пришлось исправить некоторый код, где кортежи использовались почти везде. Вместо того, чтобы думать о правильных объектных моделях, они просто использовали кортежи. Это был кошмар ... иногда я хотел убить парня, который написал код ...

Не хочу сказать, что вы не должны использовать Tuple и это зло или что-то в этом роде, и я на сто процентов уверен, что есть некоторые задачи, где Tuple - лучший кандидат для использования, но, вероятно, вам следует подумать еще раз, действительно ли вы ДЕЙСТВИТЕЛЬНО нужно это?

2
Mr. Pumpkin

Только что нашел решение одной из моих проблем в Tuple. Это похоже на объявление класса в области видимости метода, но с ленивым объявлением имен его полей. Вы оперируете коллекциями кортежей, их отдельными экземплярами, а затем создаете коллекцию анонимного типа с необходимыми именами полей, основываясь на вашем кортеже. Это позволяет избежать создания нового класса для этой цели.

Задача состоит в том, чтобы написать JSON-ответ из LINQ без каких-либо дополнительных классов:

 //I select some roles from my ORM my with subrequest and save results to Tuple list
 var rolesWithUsers = (from role in roles
                       select new Tuple<string, int, int>(
                         role.RoleName, 
                         role.RoleId, 
                         usersInRoles.Where(ur => ur.RoleId == role.RoleId).Count()
                      ));

 //Then I add some new element required element to this collection
 var tempResult = rolesWithUsers.ToList();
 tempResult.Add(new Tuple<string, int, int>(
                        "Empty", 
                         -1,
                         emptyRoleUsers.Count()
                      ));

 //And create a new anonimous class collection, based on my Tuple list
 tempResult.Select(item => new
            {
                GroupName = item.Item1,
                GroupId = item.Item2,
                Count = item.Item3
            });


 //And return it in JSON
 return new JavaScriptSerializer().Serialize(rolesWithUsers);

Конечно, мы могли бы сделать это с объявлением нового класса для моих групп, но идея создать такие анонимные коллекции без объявления новых классов.

1
Alex

Ну, в моем случае мне пришлось использовать кортеж, когда я обнаружил, что мы не можем использовать параметр в асинхронном методе. Читайте об этом здесь . Мне также нужен был другой тип возврата. Поэтому я использовал Tuple вместо моего возвращаемого типа и пометил метод как асинхронный.

Пример кода ниже.

...
...
// calling code.
var userDetails = await GetUserDetails(userId);
Console.WriteLine("Username : {0}", userDetails.Item1);
Console.WriteLine("User Region Id : {0}", userDetails.Item2);
...
...

private async Tuple<string,int> GetUserDetails(int userId)
{
    return new Tuple<string,int>("Amogh",105);
    // Note that I can also use the existing helper method (Tuple.Create).
}

Узнайте больше о Tuple здесь . Надеюсь это поможет.

1
Amogh Natu

Лучшее использование для кортежей, которое я нашел, - это когда нужно возвращать более одного типа объекта из метода, вы знаете, какие типы объектов и число они будут, и это не длинный список.

Другие простые альтернативы будут использовать параметр «out»

private string MyMethod(out object)

или сделать словарь

Dictionary<objectType1, objectType2>

Однако использование Tuple сохраняет либо создание объекта out, либо необходимость поиска записи в словаре;

1
sidjames

Ну, я попробовал 3 способа решить ту же проблему в C # 7, и я нашел вариант использования для кортежей.

Работа с динамическими данными в веб-проектах может иногда вызывать боль при отображении и т.д.

Мне нравится, что Tuple просто автоматически сопоставляется с item1, item2, itemN, что мне кажется более надежным, чем использование индексов массива, где вы можете попасть в элемент вне индекса, или использование анонимного типа, когда вы можете неправильно ввести имя свойства.

Такое ощущение, что DTO был создан бесплатно только с помощью Tuple, и я могу получить доступ ко всем свойствам с помощью itemN, который больше напоминает статическую типизацию без необходимости создания отдельного DTO для этой цели.

using System;

namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var Tuple = GetTuple();
            Console.WriteLine(Tuple.Item1);
            Console.WriteLine(Tuple.Item2);
            Console.WriteLine(Tuple.Item3);
            Console.WriteLine(Tuple);

            Console.WriteLine("---");

            var dyn = GetDynamic();
            Console.WriteLine(dyn.First);
            Console.WriteLine(dyn.Last);
            Console.WriteLine(dyn.Age);
            Console.WriteLine(dyn);

            Console.WriteLine("---");

            var arr = GetArray();
            Console.WriteLine(arr[0]);
            Console.WriteLine(arr[1]);
            Console.WriteLine(arr[2]);
            Console.WriteLine(arr);

            Console.Read();

            (string, string, int) GetTuple()
            {
                return ("John", "Connor", 1);
            }

            dynamic GetDynamic()
            {
                return new { First = "John", Last = "Connor", Age = 1 };
            }

            dynamic[] GetArray()
            {
                return new dynamic[] { "John", "Connor", 1 };
            }
        }
    }
}
0
ozzy432836

Только для прототипирования - кортежи не имеют смысла. Их удобно использовать, но это только ярлык! За прототипы - отлично. Просто обязательно удалите этот код позже.

Это легко написать, трудно читать. Он не имеет видимых преимуществ перед классами, внутренними классами, анонимными классами и т.д. 

0
bunny1985

Изменение формы объектов, когда вам нужно отправить их по проводам или передать на другой уровень приложения, и несколько объектов объединяются в один:

Пример:

var customerDetails = new Tuple<Customer, List<Address>>(mainCustomer, new List<Address> {mainCustomerAddress}).ToCustomerDetails();

Метод расширения:

public static CustomerDetails ToCustomerDetails(this Tuple<Website.Customer, List<Website.Address>> customerAndAddress)
    {
        var mainAddress = customerAndAddress.Item2 != null ? customerAndAddress.Item2.SingleOrDefault(o => o.Type == "Main") : null;
        var customerDetails = new CustomerDetails
        {
            FirstName = customerAndAddress.Item1.Name,
            LastName = customerAndAddress.Item1.Surname,
            Title = customerAndAddress.Item1.Title,
            Dob = customerAndAddress.Item1.Dob,
            EmailAddress = customerAndAddress.Item1.Email,
            Gender = customerAndAddress.Item1.Gender,
            PrimaryPhoneNo = string.Format("{0}", customerAndAddress.Item1.Phone)
        };

        if (mainAddress != null)
        {
            customerDetails.AddressLine1 =
                !string.IsNullOrWhiteSpace(mainAddress.HouseName)
                    ? mainAddress.HouseName
                    : mainAddress.HouseNumber;
            customerDetails.AddressLine2 =
                !string.IsNullOrWhiteSpace(mainAddress.Street)
                    ? mainAddress.Street
                    : null;
            customerDetails.AddressLine3 =
                !string.IsNullOrWhiteSpace(mainAddress.Town) ? mainAddress.Town : null;
            customerDetails.AddressLine4 =
                !string.IsNullOrWhiteSpace(mainAddress.County)
                    ? mainAddress.County
                    : null;
            customerDetails.PostCode = mainAddress.PostCode;
        }
...
        return customerDetails;
    }
0
Matas Vaitkevicius

Параметр out хорош, когда нужно вернуть только несколько значений, , Но когда вы начинаете встречать 4, 5, 6 или более значений, которые необходимо вернуть, он Может стать громоздким. Другим вариантом для возврата нескольких значений является создание и возврат Определенного пользователем класса/структуры или использование кортежа для упаковки всех значений, которые должны возвращаться методом.

Первый вариант, использующий класс/структуру для возврата значений, прост. Просто Создайте тип (в данном примере это структура) примерно так:

public struct Dimensions
{
public int Height;
public int Width;
public int Depth;
}

Второй вариант, использующий Tuple, является еще более элегантным решением, чем использование определяемого пользователем объекта Tuple может быть создан для хранения любого количества значений различных типов . Кроме того, данные, которые вы храните в Tuple, являются неизменяемыми; как только вы добавляете данные в кортеж через конструктор или статический метод Create, эти данные не могут быть изменены . кортежи могут принимать до восьми отдельных значений включительно. Если вам нужно вернуть Более восьми значений, вам нужно будет использовать специальный класс Tuple: Tuple Class При создании Tuple с более чем восемью значениями вы не можете использовать статический Create метод - вместо этого вы должны использовать конструктор класса. Вот как вы бы Создали кортеж из 10 целочисленных значений:

var values = new Tuple<int, int, int, int, int, int, int, Tuple<int, int, int>> (
1, 2, 3, 4, 5, 6, 7, new Tuple<int, int, int> (8, 9, 10));

Конечно, вы можете продолжать добавлять больше Tuple в конец каждого встроенного Tuple, Создавая Tuple любого размера, который вам нужен.

0
Yashwanth Chowdary Kata