Quizás este sea un buen lugar para señalar que este tema es muy antiguo. ¡Mira lo que pasó en C # 7 !
maf-soft
Respuestas:
83
Ese es el punto: es más conveniente no hacer una clase o estructura personalizada todo el tiempo. Es una mejora como Actiono Func... puedes hacer estos tipos tú mismo, pero es conveniente que existan en el marco.
Probablemente valga la pena señalar de MSDN : "Cualquier miembro estático público de este tipo es seguro para subprocesos. No se garantiza que ningún miembro de instancia sea seguro para subprocesos "
Matt Borja
Bueno, tal vez los diseñadores de idiomas deberían haber facilitado la creación de tipos personalizados sobre la marcha, entonces. ¿Entonces podríamos mantener la misma sintaxis en lugar de introducir otra?
Thomas Eyde
@ThomasEyde, que es exactamente lo que han estado haciendo durante los últimos 15 años. Así es como se agregaron miembros con cuerpo de expresión y ahora tuplas de valor. Clases de estilo de grabación estuvo a punto de la parte posterior corte en agosto (9 meses antes de este comentario) para esta versión de C # 7, y probablemente están saliendo en C # 8. También tenga en cuenta que el valor tuplas ofrecen igualdad de valor donde las clases llanura de edad, no lo hacen . Introducir todo esto en 2002 requeriría presciencia
Panagiotis Kanavos
a qué te refieres con convenient not to make a custom class. ¿Te refieres a crear una instancia de clase personalizada usando =new..()?
Alex
75
Con tuplas, podría implementar fácilmente un diccionario bidimensional (o n-dimensional para el caso). Por ejemplo, podría usar un diccionario de este tipo para implementar una asignación de cambio de moneda:
Prefiero usar un Enum para las abreviaturas de países. ¿Es eso posible?
Zack
1
Seguro, debería funcionar sin problemas ya que las enumeraciones son tipos de valor.
MarioVW
@MarioVW También se podría lograr utilizando una matriz multidimensional. ¿Cómo la tupla hace la diferencia?
Alex
26
Hay un excelente articulo en la revista MSDN que habla sobre el dolor de estómago y las consideraciones de diseño que se utilizaron para agregar Tuple al BCL. Es especialmente interesante elegir entre un tipo de valor y un tipo de referencia.
Como deja en claro el artículo, la fuerza impulsora detrás de Tuple fueron tantos grupos dentro de Microsoft que lo usaban, el equipo F # al frente. Aunque no se menciona, creo que la nueva palabra clave "dinámica" en C # (y VB.NET) también tuvo algo que ver, las tuplas son muy comunes en los lenguajes dinámicos.
De lo contrario, no es particularmente superior a crear tu propio poco, al menos puedes dar a los miembros un nombre mejor.
ACTUALIZACIÓN: debido a una gran revisión en C # versión 7, ahora obteniendo mucho más amor por la sintaxis. Anuncio preliminar en esta publicación de blog .
Aquí hay un pequeño ejemplo: digamos que tiene un método que necesita buscar el identificador y la dirección de correo electrónico de un usuario, dada una identificación 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 nuevo POCO.
Este es un buen ejemplo, sin embargo, no justifica el uso de Tuple.
Amitabh
7
Una tupla es un ajuste decente aquí ya que está devolviendo valores distintos; pero una tupla brilla más cuando devuelve varios valores de diferentes tipos .
Mark Rushakoff
21
Otro buen ejemplo sería int. TryParse, ya que podría eliminar el parámetro de salida y, en su lugar, usar una Tupla. Entonces podría haberlo hecho Tuple<bool, T> TryParse<T>(string input)y en lugar de tener que usar un parámetro de salida, obtiene ambos valores en una tupla.
Tejs
3
de hecho, eso es exactamente lo que sucede cuando llama a cualquier método TryParse desde F #.
classGrid{publicstaticint[,]Cells={{08,02,22,// whole grid omittedpublicstaticIEnumerable<Tuple<int,int,int,int>>ToList(){// code converts grid to enumeration every possible set of 4 per rules// code omitted}}
La sintaxis de tuplas 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 timevar 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, expandiremos un solo resultado en una colección de valores. La forma más sencilla de escribir esta función es:
fconvierte algún estado en una tupla. Devolvemos el primer valor de la tupla y establecemos nuestro nuevo estado en el segundo valor. Esto nos permite retener el estado durante todo el cálculo.
Lo usas como tal:
// return 0, 2, 3, 6, 8var evens =Unfold(0, state => state <10?Tuple.Create(state, state +2):null).ToList();// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34var fibs =Unfold(Tuple.Create(0,1), state =>Tuple.Create(state.Item1,Tuple.Create(state.Item2, state.Item1+ state.Item2))).Take(10).ToList();
evenses bastante sencillo, pero fibses un poco más inteligente. En staterealidad, es una tupla que contiene fib (n-2) y fib (n-1) respectivamente.
+1 Tuple.Create es una abreviatura útil paranew Tuple<Guid,string,...>
AaronLS
@Juliet Lo que el uso de la palabra clave Estado
irfandar
7
No me gusta el abuso de ellos, ya que producen código que no se explica por sí mismo, pero es increíble implementar claves compuestas sobre la marcha, ya que implementan IStructuralEquatable e IStructuralComparable (para usar tanto para búsquedas como para ordenar propósitos).
Y combinan todos los códigos hash de sus artículos, internamente; por ejemplo, aquí está el GetHashCode de Tuple (tomado de ILSpy):
Las tuplas son excelentes para realizar varias operaciones de E / S asíncronas a la vez y devolver todos los valores juntos. Aquí están los ejemplos de cómo hacerlo con y sin Tuple. ¡Las tuplas pueden hacer que tu código sea más claro!
Si de todos modos estaba usando una función anónima con un tipo implícito, entonces no está haciendo que el código sea menos claro al usar Tuple. ¿Volver a sintonizar una tupla desde un método? Úselo con moderación cuando la claridad del código 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 y torpes programadores de C # "orientados a objetos".
Las tuplas se utilizan mucho en lenguajes funcionales que pueden hacer más cosas con ellas, ahora F # es un lenguaje .net 'oficial', es posible que desee interoperar con él desde C # y pasarlas entre el código escrito en dos lenguajes.
Las tuplas también están integradas en tipos para algunos lenguajes de scripting populares como Python y Ruby (que también tienen implementaciones .Net para interoperabilidad ... IronPython / Ruby).
MutantNinjaCodeMonkey
5
Tiendo a evitarlo Tupleen la mayoría de los escenarios, ya que perjudica la legibilidad. Sin embargo, Tuplees útil cuando necesita agrupar datos no relacionados.
Por ejemplo, suponga que tiene una lista de automóviles y las ciudades en las que se compraron:
Tuplees tan útil en situaciones críticas como Point, o SomeVectorpuede ser útil al realizar operaciones gráficas. Destaca dos valores que están muy ligados entre sí. A menudo veo propiedades llamadas StartTime y EndTime al leer el código, pero incluso mejor que una fecha, una hora y una duración, una tupla te obliga a considerar ambos valores cada vez que operas en esta área de tu lógica empresarial. O devolviendo "hey, los datos han cambiado (bool), aquí están los datos (otro tipo)" en un procesamiento pesado en lugar de pasar por enlaces intensivos.
Léon Pelletier
3
Debe tener mucho cuidado con el uso Tupley probablemente pensarlo dos veces antes de hacer esto. De mi experiencia anterior descubrí que usarTuple código dificulta mucho la lectura y el soporte en el futuro. Hace un tiempo, tuve que arreglar un código donde se usaban tuplas 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 deberías usar Tupley es malo o algo así y estoy cien por ciento seguro de que hay algunas tareas en las que Tuplees el mejor candidato para ser usado, pero probablemente deberías pensarlo de nuevo, ¿REALMENTE lo necesitas? ?
El mejor uso para Tuples que he encontrado es cuando se necesita devolver más de 1 tipo de objeto de un método, sabes qué tipos de objeto y número serán, y no es una lista larga.
Otras alternativas simples serían usar un parámetro 'out'
privatestringMyMethod(outobject)
o haciendo un diccionario
Dictionary<objectType1, objectType2>
Sin embargo, el uso de una tupla ahorra la creación del objeto "out" o tener que buscar esencialmente la entrada en el diccionario;
Acabo de encontrar la solución a uno de mis problemas en Tuple. Es como declarar una clase en el alcance de un método, pero con una declaración diferida de los nombres de sus campos. Usted opera con colecciones de tuplas, sus instancias únicas y luego crea una colección de tipo anónimo con los nombres de campo requeridos, basándose en su tupla. Esto le evita crear la nueva clase para este propósito.
La tarea es escribir una respuesta JSON desde LINQ sin clases adicionales:
//I select some roles from my ORM my with subrequest and save results to Tuple listvar rolesWithUsers =(from role in roles
selectnewTuple<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 collectionvar tempResult = rolesWithUsers.ToList();
tempResult.Add(newTuple<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 JSONreturnnewJavaScriptSerializer().Serialize(rolesWithUsers);
Por supuesto, podríamos hacer esto declarando una nueva clase para mis grupos, pero la idea de crear colecciones anónimas sin declarar nuevas clases.
Bueno, en mi caso, tuve que usar una Tupla cuando descubrí que no podemos usar nuestro parámetro en un método asincrónico. Lea sobre esto aquí . También necesitaba un tipo de devolución diferente. Entonces usé una Tupla como mi tipo de retorno y marqué el método como asíncrono.
Código de muestra a continuación.
......// calling code.var userDetails =awaitGetUserDetails(userId);Console.WriteLine("Username : {0}", userDetails.Item1);Console.WriteLine("User Region Id : {0}", userDetails.Item2);......privateasyncTuple<string,int>GetUserDetails(int userId){returnnewTuple<string,int>("Amogh",105);// Note that I can also use the existing helper method (Tuple.Create).}
Supongo que no desea devolver un tipo anónimo o matriz (o contenido dinámico)
ozzy432836
0
Cambiar las formas de los objetos cuando necesita enviarlos a través de un cable o pasar a diferentes capas de aplicación y varios objetos se fusionan en uno:
Ejemplo:
var customerDetails =newTuple<Customer,List<Address>>(mainCustomer,newList<Address>{mainCustomerAddress}).ToCustomerDetails();
Un parámetro de salida 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, puede volverse difícil de manejar. Otra opción para devolver múltiples valores es crear y devolver una clase / estructura definida por el usuario o usar una Tupla para empaquetar todos los valores que deben ser devueltos por un método.
La primera opción, usar una clase / estructura para devolver los valores, es sencilla. Simplemente cree el tipo (en este ejemplo es una estructura) así:
La segunda opción, usar una tupla, es una solución aún más elegante que usar un objeto definido por el usuario. Se puede crear una tupla para contener cualquier número de valores de diferentes tipos. Además, los datos que almacena en Tuple son inmutables; una vez que agrega los datos a la Tupla a través del constructor o el método Create estático, esos datos no se pueden cambiar. Las tuplas pueden aceptar hasta ocho valores separados inclusive. Si necesita devolver más de ocho valores, deberá usar la clase Tuple especial: Clase Tuple Cuando cree una Tuple con más de ocho valores, no puede usar el método Create estático; en su lugar, debe usar el constructor de la clase. Así es como crearía una tupla de 10 valores enteros:
var values =newTuple<int,int,int,int,int,int,int,Tuple<int,int,int>>(1,2,3,4,5,6,7,newTuple<int,int,int>(8,9,10));
Por supuesto, puede continuar agregando más Tuplas al final de cada Tupla incrustada, creando Tuplas de cualquier tamaño que necesite.
Solo para la creación de prototipos: 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.
Bueno, probé 3 formas de resolver el mismo problema en C # 7 y encontré un caso de uso para Tuples.
Trabajar con datos dinámicos en proyectos web a veces puede resultar complicado al realizar mapas, etc.
Me gusta la forma en que Tuple se asignó automáticamente a item1, item2, itemN, que me parece más robusto que usar índices de matriz donde puede quedar atrapado en un elemento fuera de índice o usar el tipo anónimo donde puede escribir mal el nombre de una propiedad.
Parece que se ha creado un DTO de forma gratuita con solo usar un Tuple y puedo acceder a todas las propiedades usando itemN, que se siente más como una escritura estática sin tener que crear un DTO separado para ese propósito.
Respuestas:
Ese es el punto: es más conveniente no hacer una clase o estructura personalizada todo el tiempo. Es una mejora como
Action
oFunc
... puedes hacer estos tipos tú mismo, pero es conveniente que existan en el marco.fuente
convenient not to make a custom class
. ¿Te refieres a crear una instancia de clase personalizada usando=new..()
?Con tuplas, podría implementar fácilmente un diccionario bidimensional (o n-dimensional para el caso). Por ejemplo, podría usar un diccionario de este tipo para implementar una asignación de cambio de moneda:
fuente
Hay un excelente articulo en la revista MSDN que habla sobre el dolor de estómago y las consideraciones de diseño que se utilizaron para agregar Tuple al BCL. Es especialmente interesante elegir entre un tipo de valor y un tipo de referencia.
Como deja en claro el artículo, la fuerza impulsora detrás de Tuple fueron tantos grupos dentro de Microsoft que lo usaban, el equipo F # al frente. Aunque no se menciona, creo que la nueva palabra clave "dinámica" en C # (y VB.NET) también tuvo algo que ver, las tuplas son muy comunes en los lenguajes dinámicos.
De lo contrario, no es particularmente superior a crear tu propio poco, al menos puedes dar a los miembros un nombre mejor.
ACTUALIZACIÓN: debido a una gran revisión en C # versión 7, ahora obteniendo mucho más amor por la sintaxis. Anuncio preliminar en esta publicación de blog .
fuente
Aquí hay un pequeño ejemplo: digamos que tiene un método que necesita buscar el identificador y la dirección de correo electrónico de un usuario, dada una identificación 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 nuevo POCO.
fuente
Tuple<bool, T> TryParse<T>(string input)
y en lugar de tener que usar un parámetro de salida, obtiene ambos valores en una tupla.Usé una tupla para resolver el problema 11 del Proyecto Euler :
Ahora puedo resolver todo el problema con:
Yo podría haber utilizado un tipo personalizado para esto, pero hubiera mirado exactamente como tupla .
fuente
La sintaxis de tuplas 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:
En lugar de combinar una colección de valores en un solo resultado, expandiremos un solo resultado en una colección de valores. La forma más sencilla de escribir esta función es:
f
convierte algún estado en una tupla. Devolvemos el primer valor de la tupla y establecemos nuestro nuevo estado en el segundo valor. Esto nos permite retener el estado durante todo el cálculo.Lo usas como tal:
evens
es bastante sencillo, perofibs
es un poco más inteligente. Enstate
realidad, es una tupla que contiene fib (n-2) y fib (n-1) respectivamente.fuente
new Tuple<Guid,string,...>
No me gusta el abuso de ellos, ya que producen código que no se explica por sí mismo, pero es increíble implementar claves compuestas sobre la marcha, ya que implementan IStructuralEquatable e IStructuralComparable (para usar tanto para búsquedas como para ordenar propósitos).
Y combinan todos los códigos hash de sus artículos, internamente; por ejemplo, aquí está el GetHashCode de Tuple (tomado de ILSpy):
fuente
Las tuplas son excelentes para realizar varias operaciones de E / S asíncronas a la vez y devolver todos los valores juntos. Aquí están los ejemplos de cómo hacerlo con y sin Tuple. ¡Las tuplas pueden hacer que tu código sea más claro!
Sin (¡desagradable anidamiento!):
Con tupla
Si de todos modos estaba usando una función anónima con un tipo implícito, entonces no está haciendo que el código sea menos claro al usar Tuple. ¿Volver a sintonizar una tupla desde un método? Úselo con moderación cuando la claridad del código 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 y torpes programadores de C # "orientados a objetos".
fuente
Las tuplas se utilizan mucho en lenguajes funcionales que pueden hacer más cosas con ellas, ahora F # es un lenguaje .net 'oficial', es posible que desee interoperar con él desde C # y pasarlas entre el código escrito en dos lenguajes.
fuente
Tiendo a evitarlo
Tuple
en la mayoría de los escenarios, ya que perjudica la legibilidad. Sin embargo,Tuple
es útil cuando necesita agrupar datos no relacionados.Por ejemplo, suponga que tiene una lista de automóviles y las ciudades en las que se compraron:
Desea sumar los recuentos de cada automóvil por ciudad:
Para hacer esto, crea un
Dictionary
. Tienes pocas opciones:Dictionary<string, Dictionary<string, int>>
.Dictionary<CarAndCity, int>
.Dictionary<Tuple<string, string>, int>
.La legibilidad se pierde con la primera opción. 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 pertenezcan a una clase juntos.
La tercera opción es concisa y limpia. Es un buen uso de
Tuple
.fuente
Algunos ejemplos que se me vienen a la cabeza:
Por ejemplo, no querría incluir System.Drawing en una aplicación web solo para usar Point / PointF y Size / SizeF.
fuente
Tuple
es tan útil en situaciones críticas comoPoint
, oSomeVector
puede ser útil al realizar operaciones gráficas. Destaca dos valores que están muy ligados entre sí. A menudo veo propiedades llamadas StartTime y EndTime al leer el código, pero incluso mejor que una fecha, una hora y una duración, una tupla te obliga a considerar ambos valores cada vez que operas en esta área de tu lógica empresarial. O devolviendo "hey, los datos han cambiado (bool), aquí están los datos (otro tipo)" en un procesamiento pesado en lugar de pasar por enlaces intensivos.Debe tener mucho cuidado con el uso
Tuple
y probablemente pensarlo dos veces antes de hacer esto. De mi experiencia anterior descubrí que usarTuple
código dificulta mucho la lectura y el soporte en el futuro. Hace un tiempo, tuve que arreglar un código donde se usaban tuplas 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 deberías usar
Tuple
y es malo o algo así y estoy cien por ciento seguro de que hay algunas tareas en las queTuple
es el mejor candidato para ser usado, pero probablemente deberías pensarlo de nuevo, ¿REALMENTE lo necesitas? ?fuente
El mejor uso para Tuples que he encontrado es cuando se necesita devolver más de 1 tipo de objeto de un método, sabes qué tipos de objeto y número serán, y no es una lista larga.
Otras alternativas simples serían usar un parámetro 'out'
o haciendo un diccionario
Sin embargo, el uso de una tupla ahorra la creación del objeto "out" o tener que buscar esencialmente la entrada en el diccionario;
fuente
Acabo de encontrar la solución a uno de mis problemas en Tuple. Es como declarar una clase en el alcance de un método, pero con una declaración diferida de los nombres de sus campos. Usted opera con colecciones de tuplas, sus instancias únicas y luego crea una colección de tipo anónimo con los nombres de campo requeridos, basándose en su tupla. Esto le evita crear la nueva clase para este propósito.
La tarea es escribir una respuesta JSON desde LINQ sin clases adicionales:
Por supuesto, podríamos hacer esto declarando una nueva clase para mis grupos, pero la idea de crear colecciones anónimas sin declarar nuevas clases.
fuente
Bueno, en mi caso, tuve que usar una Tupla cuando descubrí que no podemos usar nuestro parámetro en un método asincrónico. Lea sobre esto aquí . También necesitaba un tipo de devolución diferente. Entonces usé una Tupla como mi tipo de retorno y marqué el método como asíncrono.
Código de muestra a continuación.
Lea más sobre Tuple aquí . Espero que esto ayude.
fuente
Cambiar las formas de los objetos cuando necesita enviarlos a través de un cable o pasar a diferentes capas de aplicación y varios objetos se fusionan en uno:
Ejemplo:
Método de extensión:
fuente
Un parámetro de salida 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, puede volverse difícil de manejar. Otra opción para devolver múltiples valores es crear y devolver una clase / estructura definida por el usuario o usar una Tupla para empaquetar todos los valores que deben ser devueltos por un método.
La primera opción, usar una clase / estructura para devolver los valores, es sencilla. Simplemente cree el tipo (en este ejemplo es una estructura) así:
La segunda opción, usar una tupla, es una solución aún más elegante que usar un objeto definido por el usuario. Se puede crear una tupla para contener cualquier número de valores de diferentes tipos. Además, los datos que almacena en Tuple son inmutables; una vez que agrega los datos a la Tupla a través del constructor o el método Create estático, esos datos no se pueden cambiar. Las tuplas pueden aceptar hasta ocho valores separados inclusive. Si necesita devolver más de ocho valores, deberá usar la clase Tuple especial: Clase Tuple Cuando cree una Tuple con más de ocho valores, no puede usar el método Create estático; en su lugar, debe usar el constructor de la clase. Así es como crearía una tupla de 10 valores enteros:
Por supuesto, puede continuar agregando más Tuplas al final de cada Tupla incrustada, creando Tuplas de cualquier tamaño que necesite.
fuente
Solo para la creación de prototipos: 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.
fuente
Bueno, probé 3 formas de resolver el mismo problema en C # 7 y encontré un caso de uso para Tuples.
Trabajar con datos dinámicos en proyectos web a veces puede resultar complicado al realizar mapas, etc.
Me gusta la forma en que Tuple se asignó automáticamente a item1, item2, itemN, que me parece más robusto que usar índices de matriz donde puede quedar atrapado en un elemento fuera de índice o usar el tipo anónimo donde puede escribir mal el nombre de una propiedad.
Parece que se ha creado un DTO de forma gratuita con solo usar un Tuple y puedo acceder a todas las propiedades usando itemN, que se siente más como una escritura estática sin tener que crear un DTO separado para ese propósito.
fuente