Ejemplo práctico donde se puede usar Tupla in.Net ¿4.0?


He visto la Tupla introducida en.Net 4 pero no puedo imaginar dónde se puede usar. Siempre podemos hacer una clase o Estructura personalizada.

Author: Tim, 2010-04-30

19 answers

Ese es el punto - es más conveniente no hacer una clase o estructura personalizada todo el tiempo. Es una mejora como Action o Func... puede hacer estos tipos usted mismo, pero es conveniente que existan en el marco.

 82
Author: tanascius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-28 18:29:10

Con tuplas podrías implementar fácilmente un diccionario bidimensional (o n-dimensional para el caso). Por ejemplo, podría usar un diccionario de este tipo para implementar un mapeo de cambio de divisas:

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
 72
Author: MarioVW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 20:13:56

Hay un excelente artículo en la revista MSDN que habla sobre el dolor de estómago y las consideraciones de diseño que entraron en la adición de Tupla a la BCL. Elegir entre un tipo de valor y un tipo de referencia es particularmente interesante.

Como el artículo deja claro, la fuerza impulsora detrás de Tuple fue tantos grupos dentro de Microsoft que tienen un uso para él, el equipo F# en el frente. Aunque no se menciona, considero que la nueva palabra clave "dinámica" en C# (y VB.NET) tenía algo que hacer con él, así, las tuplas son muy comunes en los lenguajes dinámicos.

De lo contrario no es particularmente superior a la creación de su propio poco, al menos se puede dar a los miembros un mejor nombre.


ACTUALIZACIÓN: debido a una gran revisión en C# versión 7, ahora recibiendo mucho más amor sintaxis. Anuncio preliminar en esta entrada del blog.

 25
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-15 13:01:40

Usé una tupla para resolver El Problema 11 del Proyecto 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
    }
}

Ahora puedo resolver todo el problema con:

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);
    }
}

Yo podríahaber usado un tipo personalizado para esto, pero se habría visto exactamente como Tupla.

 23
Author: Craig Stuntz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-30 17:05:57

Aquí hay un pequeño ejemplo: supongamos que tiene un método que necesita buscar el identificador y la dirección de correo electrónico de un usuario, dado un ID de usuario. Siempre puede crear una clase personalizada que contenga esos datos, o usar un parámetro ref / out para esos datos, o simplemente puede devolver una Tupla y tener una firma de método agradable sin tener que crear un POCO nuevo.

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
Author: Tejs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-30 15:05:00

La sintaxis de la tupla de C#es ridículamente voluminosa, por lo que las tuplas son dolorosas de declarar. Y no tiene coincidencia de patrones, por lo que también son dolorosos de usar.

Pero ocasionalmente, solo desea una agrupación ad-hoc de objetos sin crear una clase para ella. Por ejemplo, digamos que quería agregar una lista, pero quería dos valores en lugar de uno:

// 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));

En lugar de combinar una colección de valores en un solo resultado, expandamos un solo resultado en una colección de valores. El la forma más fácil de escribir esta función es:

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 convierte algún estado en una tupla. Devolvemos el primer valor de la tupla y establecemos nuestro nuevo estado al segundo valor. Esto nos permite retener el estado durante todo el cómputo.

Lo usas como tal:

// 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 es bastante sencillo, pero fibs es un poco más inteligente. Su state es en realidad una tupla que contiene fib(n-2) y fib (n-1) respectivamente.

 16
Author: Juliet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-30 16:38:32

No me gusta el abuso de ellos, ya que producen código que no se explica por sí mismo, pero son increíbles para implementar claves compuestas sobre la marcha, ya que implementan IStstructuralequatable e IStstructuralcomparable (para usar tanto para fines de búsqueda como de pedido).

Y combinan internamente todos los hashcodes de sus elementos; por ejemplo, aquí está el GetHashCode de Tupla (tomado de 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
Author: Notoriousxl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-06 19:51:49

Las tuplas son excelentes para realizar múltiples operaciones async IO a la vez y devolver todos los valores juntos. Aquí los ejemplos de hacerlo con y sin Tupla. ¡Las tuplas pueden hacer que tu código sea más claro!

Sin (anidación desagradable!):

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);
                                            }
                                        });
                            }
                        });
            }
        });

Con Tupla

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);
            }
        });

Si estabas usando una función anónima con un tipo implícito de todos modos entonces no estás haciendo que el código sea menos claro usando la Tupla. Retuning una Tupla de un método? Usar con moderación cuando el código la claridad es clave, en mi humilde opinión. Sé que la programación funcional en C# es difícil de resistir, pero tenemos que considerar todos esos viejos programadores de C # torpes "orientados a objetos".

 6
Author: AndyClaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-30 14:59:07

Las tuplas se usan mucho en lenguajes funcionales que pueden hacer más cosas con ellas, ahora F# es un lenguaje.net 'oficial' que puede que desee interoperar con él desde C# y pasarlas entre el código escrito en dos lenguajes.

 5
Author: Mant101,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-30 15:35:27

Tiendo a evitar Tuple para la mayoría de los escenarios, ya que daña la legibilidad. Sin embargo, Tuple es útil cuando necesita agrupar datos no relacionados.

Por ejemplo, supongamos que tiene una lista de automóviles y las ciudades en las que se compraron:

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

Desea agregar los recuentos para cada automóvil por ciudad:

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

Para hacer esto, se crea un Dictionary. Usted tiene algunas opciones:

  1. Crea un Dictionary<string, Dictionary<string, int>>.
  2. Crea un Dictionary<CarAndCity, int>.
  3. Crear un Dictionary<Tuple<string, string>, int>.

La legibilidad se pierde con la primera opción. Se requerirá que escriba mucho más código.

La segunda opción funciona y es sucinta, pero el coche y la ciudad no están realmente relacionados y probablemente no pertenecen a una clase juntos.

La tercera opción es sucinta y limpia. Es un buen uso de Tuple.

 5
Author: John Kurlak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-09 17:03:03

Algunos ejemplos de la parte superior de mi cabeza:

  • Una ubicación X e Y (y Z si quieres)
  • a Anchura y Altura
  • Cualquier cosa medida en el tiempo

Por ejemplo, no querrá incluir System.Dibujar en una aplicación web solo para usar Point / PointF y Size / SizeF.

 4
Author: James Westgate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-30 17:11:59

Usted debe tener mucho cuidado con el uso de Tuple y probablemente pensar dos veces antes de hacer esto. De mi experiencia anterior descubrí que usar Tuple hace que el código sea muy difícil de leer y soportar en el futuro. Hace un tiempo, tuve que arreglar algún código donde las tuplas se usaban en casi todas partes. En lugar de pensar en modelos de objetos adecuados, solo usaron tuplas. Eso fue una pesadilla... a veces quería matar al tipo que escribió el código...

No quiero decir que no debes usar Tuple y es malvado o algo así y estoy cien por ciento seguro de que hay algunas tareas donde el Tuple es el mejor candidato para ser utilizado, pero probablemente deberías pensar de nuevo, ¿realmente lo necesitas?

 2
Author: Mr. Pumpkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-01 12:58:23

El mejor uso para las Tuplas que he encontrado es cuando necesita devolver más de 1 tipo de objeto de un método, sabe qué tipos de objeto y número serán, y no es una lista larga.

Otras alternativas simples usarían un parámetro ' out '

private string MyMethod(out object)

O hacer un Diccionario

Dictionary<objectType1, objectType2>

Sin embargo, usar una tupla ahorra crear el objeto' out ' o tener que buscar esencialmente la entrada en el diccionario;

 1
Author: sidjames,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-02 19:14:31

Acabo de encontrar la solución de uno de mis problemas en Tupla. Es como declarar una clase en el alcance de un método, pero con una declaración perezosa de sus nombres de campos. Usted opera con colecciones de tuplas, sus instancias individuales y luego crea una colección de tipo anónimo con los nombres de campo requeridos, basándose en su tupla. Esto evita que cree la nueva clase para este propósito.

La tarea es escribir una respuesta JSON desde LINQ sin ninguna clase adicional:

 //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);

De la causa podríamos hacer esto con declarar una nueva Clase para mis grupos, pero la idea de crear colecciones tan anónimas sin declarar nuevas clases.

 1
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-30 10:53:43

Bueno, en mi caso, tuve que usar una Tupla cuando descubrí que no podemos usar el parámetro out en un método asíncrono. Lea sobre ello aquí. También necesitaba un tipo de devolución diferente. Así que usé una Tupla en su lugar como mi tipo de retorno y marcé el método como async.

Código de ejemplo a continuación.

...
...
// 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).
}

Lea más sobre Tupla aquí. Espero que esto ayude.

 1
Author: Amogh Natu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-23 15:06:48

Cambiar las formas de los objetos cuando necesita enviarlos a través de cable o pasarlos a diferentes capas de aplicación y varios objetos se fusionan en uno:

Ejemplo:

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

Método de extensión:

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
Author: Matas Vaitkevicius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-10 12:07:12

Un parámetro out es excelente cuando solo hay unos pocos valores que deben devolverse, pero cuando comienza a encontrar 4, 5, 6 o más valores que deben devolverse, se puede ser difícil de manejar. Otra opción para devolver varios valores es crear y volver una clase/estructura definida por el usuario o usar una Tupla para empaquetar todos los valores que necesitan para ser devuelto por un método.

La primera opción, usando una clase/estructura para devolver los valores, es sencilla. Simplemente crear el tipo (en este ejemplo es una estructura) así:

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

La segunda opción, usando una Tupla, es una solución aún más elegante que usando un userdefined objeto. Se puede crear una Tupla para contener cualquier número de valores de diferentes tipos. Además, los datos que almacena en la Tupla son inmutables; una vez que agrega los datos a la Tupla a través del constructor o el método Static Create, esos datos no pueden ser cambiar. Las tuplas pueden aceptar hasta ocho valores separados. Si necesita volver mas que ocho valores, usted necesitará utilizar la clase especial de Tupla: Clase de Tupla Al crear una Tupla con más de ocho valores, no puede usar la creación estática método: en su lugar, debe usar el constructor de la clase. Así es como lo harías cree una Tupla de 10 valores enteros:

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));

Por supuesto, puede continuar agregando más Tuplas al final de cada Tupla incrustada, creando cualquier tupla de tamaño que necesites.

 0
Author: Yashwanth Chowdary Kata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-16 05:18:11

Solo para prototipado - las tuplas no tienen sentido. Es conveniente usarlos, pero es solo un atajo! Para prototipos-bien. Solo asegúrese de eliminar este código más tarde.

Es fácil de escribir, difícil de leer. No tiene ventajas visibles sobre las clases, clases internas, clases anónimas, etc.

 0
Author: bunny1985,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-04 13:56:40

Bueno, he intentado 3 maneras de resolver el mismo problema en C#7 y he encontrado un caso de uso para las Tuplas.

Trabajar con datos dinámicos en proyectos web a veces puede ser un dolor al mapear, etc.

Me gusta la forma en que la Tupla se asigna automáticamente a item1, item2, itemN, que me parece más robusto que usar índices de matriz donde podría quedar atrapado en un elemento fuera de índice o usar el tipo anónimo donde puede escribir mal un nombre de propiedad.

Parece que se ha creado un DTO para gratis solo mediante el uso de una Tupla y puedo acceder a todas las propiedades utilizando itemN que se siente más como la escritura estática sin tener que crear un DTO separado para ese propósito.

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
Author: ozzy432836,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-09 23:29:34