Cuándo usar: Tupla vs Clase c # 7.0


Antes de las tuplas, solía crear un class y sus variables luego crear un objeto a partir de esta clase y hacer que ese objeto sea el tipo de retorno para algunas funciones.

Ahora con las tuplas puedo hacer lo mismo y en c # 7.0 podemos asignar nombres comprensibles para las propiedades de las tuplas (antes era item1, item2, etc..)

Así que ahora me pregunto, ¿cuándo debo usar tuplas y cuándo debo crear una clase en c# 7.0?

Author: Paulo Morgado, 2017-06-20

9 answers

Como esta respuesta está causando cierta confusión entre algunas personas aquí, debo aclarar que, según la pregunta, todas las referencias a "tupla" aquí se refieren al tipo ValueTuple y a las nuevas características de azúcar sintáctica de la tupla de C# 7 y de ninguna manera se refieren a los antiguos tipos de referencia System.Tuple.

Así que ahora me pregunto, ¿cuándo debo usar tuplas y cuándo debo crear una clase en c# 7.0?

Solo usted puede realmente responder a esa pregunta, ya que realmente depende de su código.

Sin embargo, hay pautas y reglas que puedes seguir para guiarte en la elección entre ellas:

Las tuplas son valores, por lo que se copian por valor, en lugar de por referencia.

La mayoría de las veces, esto no debería ser un problema. Sin embargo, si está pasando tuplas de estructuras grandes, esto podría tener un impacto en el rendimiento. Ref locals / returns se pueden utilizar para solucionar estos problemas de rendimiento, sin embargo.

Además, debido a que son valores, modificar una copia remotamente no cambiará la copia original. Esto es algo bueno, pero podría atrapar a algunas personas.

Los nombres de los elementos de tupla no se conservan

Los nombres dados a los elementos son usados por el compilador y (en la mayoría de los casos) no están disponibles en tiempo de ejecución. Esto significa que la reflexión no se puede usar para descubrir sus nombres; no se puede acceder a ellos dinámicamente y no se pueden usar en vistas razor.

También esta es una consideración importante con las API. Una tupla devuelta desde un método es la excepción a la regla relativa a la detección de nombres después de la compilación. El compilador agrega atributos al método que contienen información sobre los nombres de tupla. Esto significa que puede devolver de forma segura una tupla desde un método público en un ensamblado y acceder a sus nombres en otro.

Las tuplas son ligeras

Las tuplas son mucho más simples de escribir que los tipos, ya que son menos detalladas y la declaración puede ser "inlineada" (es decir, declarada en el punto de uso). Esto funciona bien al declarar un método que devuelve varios valores, por ejemplo.

Sin embargo, debido a que se declaran en el punto de uso, si tiene MethodA que llama MethodB que llama MethodC y cada uno devuelve una tupla, necesitará redefinir la tupla en cada etapa. No hay (todavía) una forma de crear un alias de una tupla y reutilizarlo a través de múltiples métodos.

Solo usa el sentido común

Para cualquier situación en la que pueda considerar usar una tupla: simplemente hágase la pregunta: "will una tupla simplifica el código aquí". Si la respuesta es "sí", entonces use uno. Y que en última instancia es la consideración principal sobre si utilizar una tupla o una clase personalizada.

 23
Author: David Arno,
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-06-20 16:39:28

En términos generales, las clases con nombre tienen algún significado en el diseño de su sistema. También son más detallados para escribir. Por ejemplo, puede tener una clase llamada MediaFileOpener. Es importante para el diseño que sepamos lo que hace esta clase - ¡estamos trabajando con archivos multimedia!

Los tipos y tuplas anónimos se utilizan cuando no hay significado de diseño y todo lo que desea es un Objeto de transferencia de datos ligero (DTO) para mover información.

Como regla, si su clase requiere documentación para describir para qué sirve, o si hay un comportamiento que proporciona, use una clase completa. Si todo lo que necesita es almacenamiento temporal o algún tipo de agrupación, use una Tupla. Considere una situación en la que desea devolver más de un valor de un método asincrónico. Tuple está diseñado para resolver ese problema.

 16
Author: Gusdor,
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-06-20 10:54:14

Usar una clase

Si sus objetos son entidades que se utilizan ampliamente en toda su aplicación y también se almacenan en algún tipo de almacenamiento persistente como una base de datos relacional (SQL Server, MySQL, SQLite), una base de datos NoSQL o Caché (Redis, Azure DocumentDB) o incluso en archivos de texto simples o CSV.

Así que sí, cualquier cosa persistente debe tener su propia clase.

Usar una Tupla

Si sus objetos son de corta duración sin un significado especial para su aplicación. Por ejemplo, si necesita devolver rápidamente un par de coordenadas, es mejor tener algo como esto:

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

Que definir una clase separada

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

Una Tupla le ahorrará tiempo de tener que asignar memoria en el montón usando new para una operación tan simple.

Otro momento en el que encuentro tuplas útiles es cuando se realizan múltiples operaciones matemáticas en algunos operandos

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)

No tiene sentido definir una clase en este caso. No importa cuáles son los cálculos los resultados no pertenecen a una clase. Así que la alternativa sería usar variables out pero creo que las tuplas son más expresivas y resultan en una mejor legibilidad.

 3
Author: dimlucas,
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-06-20 10:59:51

Las tuplas están destinadas a representar múltiples valores, como cuando un método intenta devolver múltiples valores. El soporte de tupla en C# 7 utiliza instancias System.ValueTuple<...> para representar ese conjunto de valores. Los nombres de esos valores son válidos solo en el contexto en el que se están utilizando y no se aplican.

Las clases están destinadas a representar un solo valor con varios atributos.

 2
Author: Paulo Morgado,
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-06-20 10:58:59

Generalmente desea tener una clase cuando su objeto se va a usar en otro lugar o si representa un objeto real o un concepto en su dominio. Probablemente vas a crear una clase para representar un automóvil o una tienda de automóviles, no tuplas.

Por otro lado, a veces solo desea devolver un par de objetos de un método. Tal vez no representan nada especial, es solo que necesitas devolverlos juntos en ese método en particular. A veces, incluso si lo hacen representa un concepto de tu dominio (digamos que estás devolviendo (Car, Store), que podría representarse como un objeto Sale), en realidad no los vas a usar en ningún lugar you solo estás moviendo datos. En esos casos, está bien usar tuplas.

Ahora, hablando de C# específicamente, hay una cosa más que debes saber. El tipo de tupla de C # 7 es en realidad el ValueTuple, que es una estructura. A diferencia de las clases, que son tipos de referencia, las estructuras son tipos de valor. Puedes leer más sobre eso en msdn . Lo más importante es saber que pueden implicar una gran cantidad de copias, así que tenga cuidado.

 2
Author: andre_ss6,
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-06-20 11:00:01

Quiero comenzar mencionando que C# ya tenía soporte para tipos anónimos. Que son tipos de referencia. Por lo tanto, ya tenía una alternativa adecuada para crear una clase con nombre.

Una ventaja de una clase con nombre es que será más fácil de reutilizar (por ejemplo, si necesita el mismo tipo en varios lugares) y documento. Dado que el tipo anónimo es anónimo, solo puede obtener variables escritas en él si puede usar var, lo que limita los contextos en los que los tipos anónimos son útiles (por ejemplo, no se pueden usar como tipos de campo, tipos de retorno o tipos de parámetros).

Por supuesto, usted podría ir sobre algunas de las limitaciones de los tipos anónimos mediante el uso de System.Tuple. Que también es un tipo de referencia, y se puede utilizar explícitamente. La desventaja es que carece de nombres personalizados para los miembros.


C # 7 tuplas (ValueTuple) puede considerarse similar a los tipos anónimos. La primera diferencia es que son tipos de valor. Esto significa que estas tuplas tendrán ventaja de rendimiento siempre y cuando permanezcan en el ámbito local o se muevan a través de la pila (que es el uso común de los tipos anónimos, debido a su limitación).

La segunda diferencia es que la nueva sintaxis permite que las tuplas aparezcan en más lugares que los tipos anónimos, como sabes, tienes azúcar sintáctica para definir el tipo de retorno a ValueTuple (mientras usas tipos anónimos tenías que devolver object).

La tercera diferencia es que ValueTuple soporta deconstrucción fuera de la caja. Para citar ¿Qué hay de nuevo en C# 7.0:

Otra forma de consumir tuplas es deconstruirlas. Una declaración deconstruyendo es una sintaxis para dividir una tupla (u otro valor) en sus partes y asignar esas partes individualmente a variables frescas:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Que también puede hacer con tipos personalizados agregando un método Deconstruct.


Para resumen:

  • ValueTuple tiene azúcar sintáctico en C # 7.0, que debe considerarse para su legibilidad.
  • ValueTuple es un tipo de valor. Se aplican todos los pros y los contras entre el uso de a class y a struct.
  • ValueTuple se puede usar explícitamente (con o sin el azúcar sintáctico) lo que le permite tener la versatilidad de System.Tuple mientras conserva miembros nombrados.
  • ValueTuple soporta la deconstrucción.

Dado que debe de ella es azúcar sintáctica, yo diría que el argumento más fuerte para elegir ValueTuple es el mismo argumento para elegir un struct. Lo que sería ideal para tipos pequeños e inmutables, que viven principalmente en la pila (por lo que no tiene mucho boxeo y unboxing).

Comparando ValueTuple con una estructura completa, considerando el azúcar sintáctico, sugeriría usar ValueTuplepor defecto, a menos que necesite un diseño explícito o necesite agregar métodos a él.

También quiero decir que el azúcar sintáctico no mejora necesariamente la legibilidad. La razón principal es que usted no está nombrando el tipo, y el nombre del tipo proporciona significado al código. Además de eso, puede agregar documentación a una declaración struct o class que facilite la comprensión.

Con todo, la situación donde ValueTuple realmente brilla es devolver múltiples valores de un método. En este caso se elimina la necesidad de crear nuevos parámetros out. Y la documentación del ValueTuple utilizado puede vivir en la documentación del método. Si encuentra que necesita hacer algo más con el ValueTuple (definiendo métodos de extensión, por ejemplo), sugeriría considerar la creación de un tipo con nombre en su lugar.

 2
Author: Theraot,
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-06-20 11:36:00

La tupla es una gran opción cuando desea combinar varios valores (pueden ser tipos diferentes) en un objeto sin crear una clase personalizada. En este caso, Tupla sería una opción rápida y perfecta para ir con.

 1
Author: Qasim Bataineh,
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
2018-03-15 10:43:25

Creo que esto se va a convertir en una pregunta que se hace mucho. Actualmente no hay una "mejor práctica" para cuándo usar las tuplas de nuevo valor frente a una clase.

Sin embargo, vale la pena leer lo que surgió durante conversaciones anteriores sobre las versiones anteriores de tuplas vs una clase.

En mi opinión, la tupla de valores solo debe usarse mínimamente y con no más de un máximo de tres valores. Creo que esto logra un buen equilibrio de " devolver algunos valores sin el necesidad de una clase" y "horrible lío de valores". Si tiene más de tres valores que devolver, cree una clase.

Nunca usaría una tupla para volver de una API pública que los consumidores tendrán que usar. De nuevo, usa una clase.

Aquí hay un código del mundo real que he usado:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

Tan pronto como quiero devolver datos más complejos, hago una clase.

 0
Author: user9993,
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-06-20 11:01:46

Evitaría usar tuplas como un tipo de retorno de métodos públicos. En tales casos preferiría definir una clase o estructura.

 0
Author: Frederik Gheysels,
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
2018-01-23 13:16:08