Considere una función que devuelve dos valores. Podemos escribir:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
¿Cuál es la mejor práctica y por qué?
struct
como seEric
mencionó.Respuestas:
Cada uno tiene sus pros y sus contras.
Los parámetros de salida son rápidos y económicos, pero requieren que pase una variable y confíe en la mutación. Es casi imposible utilizar correctamente un parámetro de salida con LINQ.
Las tuplas ejercen presión sobre la recolección de basura y no se documentan por sí mismas. "Item1" no es muy descriptivo.
Las estructuras personalizadas pueden ser lentas de copiar si son grandes, pero se autodocumentan y son eficientes si son pequeñas. Sin embargo, también es complicado definir un montón de estructuras personalizadas para usos triviales.
Me inclinaría a la solución de estructura personalizada si todas las demás cosas son iguales. Aún mejor, es hacer una función que solo devuelva un valor . ¿Por qué devuelve dos valores en primer lugar?
ACTUALIZACIÓN: Tenga en cuenta que las tuplas en C # 7, que se enviaron seis años después de que se escribió este artículo, son tipos de valor y, por lo tanto, es menos probable que creen presión de recolección.
fuente
Además de las respuestas anteriores, C # 7 trae tuplas de tipo de valor, a diferencia de
System.Tuple
que es un tipo de referencia y también ofrece una semántica mejorada.Aún puede dejarlos sin nombre y usar la
.Item*
sintaxis:(string, string, int) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.Item1; //John person.Item2; //Doe person.Item3; //42
Pero lo realmente poderoso de esta nueva característica es la capacidad de tener tuplas con nombre. Entonces podríamos reescribir lo anterior así:
(string FirstName, string LastName, int Age) getPerson() { return ("John", "Doe", 42); } var person = getPerson(); person.FirstName; //John person.LastName; //Doe person.Age; //42
También se admite la desestructuración:
(string firstName, string lastName, int age) = getPerson()
fuente
Creo que la respuesta depende de la semántica de lo que está haciendo la función y la relación entre los dos valores.
Por ejemplo, los
TryParse
métodos toman unout
parámetro para aceptar el valor analizado y devuelven unbool
para indicar si el análisis se realizó correctamente o no. Los dos valores realmente no van juntos, por lo que, semánticamente, tiene más sentido y la intención del código es más fácil de leer para usar elout
parámetro.Sin embargo, si su función devuelve coordenadas X / Y de algún objeto en la pantalla, entonces los dos valores pertenecen semánticamente juntos y sería mejor usar a
struct
.Personalmente, evitaría usar un
tuple
para cualquier cosa que sea visible para el código externo debido a la sintaxis incómoda para recuperar los miembros.fuente
out
parámetronull
. Hay algunos tipos inmutables que aceptan valores NULL.try
patrón que funciona con interfaces covariantes esT TryGetValue(whatever, out bool success)
; ese enfoque habría permitido interfacesIReadableMap<in TKey, out TValue> : IReadableMap<out TValue>
y permitiría que el código que quiere mapear instancias deAnimal
a instancias deCar
acepte unDictionary<Cat, ToyotaCar>
[usingTryGetValue<TKey>(TKey key, out bool success)
. No es posible tal variación siTValue
se utiliza comoref
parámetro.Seguiré con el enfoque de usar el parámetro Out porque en el segundo enfoque necesitaría crear un objeto de la clase Tuple y luego agregarle valor, lo que creo que es una operación costosa en comparación con devolver el valor en el parámetro out. Aunque si desea devolver varios valores en Tuple Class (que de hecho no se puede lograr simplemente devolviendo un parámetro), optaré por un segundo enfoque.
fuente
out
. Además, hay unaparams
palabra clave que no mencioné para aclarar la pregunta.No mencionaste una opción más, que es tener una clase personalizada en lugar de una estructura. Si los datos tienen una semántica asociada que puede ser operada por funciones, o el tamaño de la instancia es lo suficientemente grande (> 16 bytes como regla general), se puede preferir una clase personalizada. No se recomienda el uso de "out" en la API pública debido a su asociación con punteros y requiere comprender cómo funcionan los tipos de referencia.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tuple es bueno para uso interno, pero su uso es incómodo en API públicas. Entonces, mi voto es entre estructura y clase para API pública.
fuente
No existe una "mejor práctica". Es con lo que se siente cómodo y lo que funciona mejor en su situación. Siempre que sea coherente con esto, no hay ningún problema con ninguna de las soluciones que ha publicado.
fuente