Pregunta

Me encuentro ocasionalmente en C # 3.0 buscando formas de simular la noción de una tupla. Con el tiempo he tenido varios "pobres". implementaciones, aquí hay algunas de ellas:

Matriz básica de objetos :

object[] poorTuple = new object[]{foo,bar,baz}; // basic object array

Más fuertemente tipeado , HoHoHo ...

KeyValuePair<TypeA, KeyValuePair<TypeB, TypeC>> poorTuple;

Implementación de una clase que puede usar inferencia de tipos (levantada de Programación funcional para Mundo real )

public static class Tuple{
  public static Tuple<T1, T2> Create<T1 foo, T2 bar>{
    return new Tuple<T1, T2>(foo, bar);
  }
}
// later: 
var data = Tuple.Create("foo", 42);

Preguntas:

  1. Cualquier otra forma de tener la tupla de un hombre pobre en C # 3.0 (o lenguaje de elección que carece de la estructura de datos).

  2. ¿Cuál es la mejor forma de obtener una tupla en C # 3.0? Si alguien tiene una recomendación de biblioteca, es bienvenida.

  3. ¿En qué punto (sí, generalizar para mí) tiene sentido crear un tipo específico en lugar de algo como una lista o tupla? (buscando reglas generales)

¿Fue útil?

Solución

Puede crear tipos anónimos que funcionen de manera similar a las tuplas, excepto con nombres útiles:

var itemsWithChildCounts 
    = myObjects.Select(x => new { Name = x.Name, Count = x.Children.Count() });

Otros consejos

Aquí está el código para una tupla genérica tomada de Bill a Wagner artículo en la edición de abril de 2007 de Visual Studio Magazine .

public struct Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
{
    private readonly T1 first;
    public T1 First
    {
        get { return first; }
    }

    private readonly T2 second;
    public T2 Second
    {
        get { return second; }
    } 

    public Tuple(T1 f, T2 s)
    {
        first = f;
        second = s;
    }

    #region IEquatable<Tuple<T1,T2>> Members
    public bool Equals(Tuple<T1, T2> other)
    {
        return first.Equals(other.first) && 
            second.Equals(other.second);
    }

    public override bool Equals(object obj)
    {
        if (obj is Tuple<T1, T2>)
            return this.Equals((Tuple<T1, T2>)obj);
        else
            return false;
    }

    public override int GetHashCode()
    {
        return first.GetHashCode() ˆ second.GetHashCode();
    }
    #endregion
}

Para 1 - 2: prefiero implementar su propia clase de tupla. La implementación que robó es decente. Debería funcionar bien.

Para 3: esta es mi regla general: tan pronto como vaya a reutilizar esta funcionalidad en varios métodos (con los mismos tipos que tengan el mismo significado), o si la usa en cualquier API pública, creo que es hora de implementar un "real" clase con sus tipos específicos.

  1. El " Implementando una clase " el método es tan bueno como va a ser (y de todos modos debería estar en la próxima versión de .NET)
  2. No tengo idea, los guardo en mi propia biblioteca personal de clases por ahora.
  3. Consideraría crear una clase adecuada para ellos tan pronto como se expongan al público (es decir, cuando ya no se usen solo para almacenar valores relacionados para una clase internamente).

Dado que solicitó una opinión, la mía sería crear siempre un tipo; no puedo encontrar una razón para no hacerlo.

La mayoría de las veces puede encontrar que realmente necesitaba el tipo (el uso principal es almacenar dos elementos en una colección o devolver dos elementos de una llamada al método, en ambos casos si los elementos no están estrechamente relacionados , probablemente estás haciendo algo mal).

Creo que es bueno crear un nuevo tipo cuando tiene sentido introducir un nuevo conjunto de valores en su programa. Por ejemplo, si está escribiendo una calculadora compleja, haga un tipo de número complejo. Si realmente solo quieres '' pegar '' algunas variables juntas por un momento, entonces una tupla es probablemente una mejor opción. Digamos que tiene una función simple que reúne dos números de la consola ... podría hacer algo como:

static void GetNumbers(out int x, out int y) { ... }
...
int x, y;
GetNumbers(out x, out y);

Creo que generalmente tiene sentido cuando un "getter" La función tiene un valor de retorno, pero en este caso no lo tiene porque realmente no puedo tener dos valores de retorno. Podría ir y hacer un nuevo tipo llamado TwoNumbers y usarlo, pero creo que esto rápidamente se convierte en un problema más que una solución. Si C # tuviera tuplas, podría hacer algo como lo siguiente:

static (int, int) GetNumbers() { ... }
...
int x, y;
(x, y) = GetNumbers();

Ahora la pregunta realmente interesante es: aunque C # no tiene esta función, ¿puedo implementarla yo mismo con una biblioteca? El código que sugiere es un comienzo, pero no me permitirá asignar como lo hice en el segundo ejemplo. No estoy seguro acerca de C #, pero puede acercarse mucho a esto en C ++ al usar el hecho de que una llamada de función puede ser el operando izquierdo para el operador de asignación cuando su valor de retorno es un tipo de referencia . Considere lo siguiente:

// this class is implemented much like yours only with C++
template<typename T1, typename T2> class tuple { ... }
...
template<typename T1, typename T2> tuple<T1, T2>& tie(T1 a, T2 b) { ... }
...
template<typename T1, typename T2> tuple<T1, T2> get_numbers() { ... }
...
int x, y;
tie(x, y) = get_numbers();

Diría que está bastante cerca ... en lugar de (x, y) = tenemos tie (x, y) = . Si está interesado en los detalles de implementación, lo referiré a la biblioteca TR1 donde aprendí por primera vez sobre esto:

http: //www.boost .org / doc / libs / 1_41_0 / libs / tuple / doc / tuple_users_guide.html # niveles

Entonces, para traer todo esto de vuelta a C # 3.0 ... ciertamente podemos crear una clase de tupla genérica como usted ha mostrado, pero ¿podemos crear una función como vincular para "desempaquetar"? la tupla en C #? No lo he probado, suena divertido. Sin algo así, sería reacio a permitir que una biblioteca de tuplas C # prolifere a través de mi código.

No me gusta la inferencia de tipos en C # ya que se abusa demasiado de ella para escribir de forma más corta. Por supuesto, es una característica muy interesante, pero la gente abusa de mi en mi humilde opinión como en este caso.

En este caso, especificaría el tipo explícitamente para evitar confusiones sobre el tipo (es decir, tal vez 42 es un largo, un byte o un corto).

Entonces, ¿por qué no tener una clase de tupla simple, que se puede implementar en solo unas pocas líneas? Y si eres flojo, incluso puedes escribir algunos métodos de extensión para tu clase de tupla. Esto hace la vida más fácil pero también más limpia.

No veo el punto de tener una "fantasía" clase de tupla en lugar de la genérica que presentó (excepto por la inferencia de tipos).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top