¿Qué es el análogo de C # de C ++ std :: pair?

284

Estoy interesado: ¿Cuál es el análogo de std::pairC # en C ++? Encontré System.Web.UI.Pairclase, pero preferiría algo basado en plantillas.

¡Gracias!

Alexander Prokofyev
fuente
11
Tuve la misma solicitud hace un tiempo, pero cuanto más lo pensaba, es posible que desee simplemente rodar su propia clase de emparejamiento, con tipos y campos de clase explícitos en lugar del genérico "Primero" y "Segundo". Hace que su código sea más legible. Una clase de emparejamiento puede tener tan solo 4 líneas, por lo que no está ahorrando mucho reutilizando una clase genérica Pair <T, U> y su código será más legible.
Mark Lakata

Respuestas:

325

Las tuplas están disponibles desde .NET4.0 y admiten genéricos:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);

En versiones anteriores puede usar System.Collections.Generic.KeyValuePair<K, V>o una solución como la siguiente:

public class Pair<T, U> {
    public Pair() {
    }

    public Pair(T first, U second) {
        this.First = first;
        this.Second = second;
    }

    public T First { get; set; }
    public U Second { get; set; }
};

Y úsalo así:

Pair<String, int> pair = new Pair<String, int>("test", 2);
Console.WriteLine(pair.First);
Console.WriteLine(pair.Second);

Esto produce:

test
2

O incluso este par encadenado:

Pair<Pair<String, int>, bool> pair = new Pair<Pair<String, int>, bool>();
pair.First = new Pair<String, int>();
pair.First.First = "test";
pair.First.Second = 12;
pair.Second = true;

Console.WriteLine(pair.First.First);
Console.WriteLine(pair.First.Second);
Console.WriteLine(pair.Second);

Eso da salida:

test
12
true
Jorge Ferreira
fuente
Vea mi publicación sobre cómo agregar un método Equals
Andrew Stein
Tuple <> es ahora una mejor solución.
dkantowitz
66
Dado que los parámetros de tipo que pertenecen a la clase genérica no se pueden inferir en una expresión de creación de objetos (llamada de constructor), los autores de BCL crearon una clase auxiliar no genérica llamada Tuple. Por lo tanto, puede decir Tuple.Create("Hello", 4)cuál es un poco más fácil que new Tuple<string, int>("Hello", 4). (Por cierto, .NET4.0 ya está aquí desde 2010.)
Jeppe Stig Nielsen
44
Eso sí, Tuple<>implementa una semántica sólida Equalsy GetHashCodecon valor, lo cual es genial. Tenga en cuenta al implementar sus propias tuplas.
nawfal
Obviamente, esto se rompe debido a Equals y GetHashCode
julx
90

System.Web.UIcontenía la Pairclase porque se usaba mucho en ASP.NET 1.1 como una estructura ViewState interna.

Actualización de agosto de 2017: C # 7.0 / .NET Framework 4.7 proporciona una sintaxis para declarar una Tupla con elementos con nombre utilizando la System.ValueTupleestructura.

//explicit Item typing
(string Message, int SomeNumber) t = ("Hello", 4);
//or using implicit typing 
var t = (Message:"Hello", SomeNumber:4);

Console.WriteLine("{0} {1}", t.Message, t.SomeNumber);

ver MSDN para más ejemplos de sintaxis.

Actualización de junio de 2012: Tuples forma parte de .NET desde la versión 4.0.

Aquí hay un artículo anterior que describe la inclusión en .NET4.0 y el soporte para genéricos:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
Peatón imprudente
fuente
2
Tenga en cuenta que las tuplas son de solo lectura. Es decir, no puedes hacer esto:tuple.Item1 = 4;
skybluecodeflier
2
Las tuplas son exactamente lo que estaba buscando. Gracias.
gligoran
38

Lamentablemente, no hay ninguno. Puede usar el System.Collections.Generic.KeyValuePair<K, V>en muchas situaciones.

Alternativamente, puede usar tipos anónimos para manejar tuplas, al menos localmente:

var x = new { First = "x", Second = 42 };

La última alternativa es crear una clase propia.

Konrad Rudolph
fuente
2
Para ser claros, los tipos anónimos también son de solo lectura: msdn .
bsegraves
11

Algunas respuestas parecen simplemente incorrectas

  1. no puede usar el diccionario cómo almacenaría los pares (a, b) y (a, c). El concepto de pares no debe confundirse con la búsqueda asociativa de claves y valores
  2. gran parte del código anterior parece sospechoso

Aquí está mi clase de pareja

public class Pair<X, Y>
{
    private X _x;
    private Y _y;

    public Pair(X first, Y second)
    {
        _x = first;
        _y = second;
    }

    public X first { get { return _x; } }

    public Y second { get { return _y; } }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        Pair<X, Y> other = obj as Pair<X, Y>;
        if (other == null)
            return false;

        return
            (((first == null) && (other.first == null))
                || ((first != null) && first.Equals(other.first)))
              &&
            (((second == null) && (other.second == null))
                || ((second != null) && second.Equals(other.second)));
    }

    public override int GetHashCode()
    {
        int hashcode = 0;
        if (first != null)
            hashcode += first.GetHashCode();
        if (second != null)
            hashcode += second.GetHashCode();

        return hashcode;
    }
}

Aquí hay un código de prueba:

[TestClass]
public class PairTest
{
    [TestMethod]
    public void pairTest()
    {
        string s = "abc";
        Pair<int, string> foo = new Pair<int, string>(10, s);
        Pair<int, string> bar = new Pair<int, string>(10, s);
        Pair<int, string> qux = new Pair<int, string>(20, s);
        Pair<int, int> aaa = new Pair<int, int>(10, 20);

        Assert.IsTrue(10 == foo.first);
        Assert.AreEqual(s, foo.second);
        Assert.AreEqual(foo, bar);
        Assert.IsTrue(foo.GetHashCode() == bar.GetHashCode());
        Assert.IsFalse(foo.Equals(qux));
        Assert.IsFalse(foo.Equals(null));
        Assert.IsFalse(foo.Equals(aaa));

        Pair<string, string> s1 = new Pair<string, string>("a", "b");
        Pair<string, string> s2 = new Pair<string, string>(null, "b");
        Pair<string, string> s3 = new Pair<string, string>("a", null);
        Pair<string, string> s4 = new Pair<string, string>(null, null);
        Assert.IsFalse(s1.Equals(s2));
        Assert.IsFalse(s1.Equals(s3));
        Assert.IsFalse(s1.Equals(s4));
        Assert.IsFalse(s2.Equals(s1));
        Assert.IsFalse(s3.Equals(s1));
        Assert.IsFalse(s2.Equals(s3));
        Assert.IsFalse(s4.Equals(s1));
        Assert.IsFalse(s1.Equals(s4));
    }
}
Antonio
fuente
3
Si no implementa IEquatable, obtendrá el boxeo. Hay más trabajo por hacer para completar su clase correctamente.
Jack
8

Si se trata de diccionarios y similares, está buscando System.Collections.Generic.KeyValuePair <TKey, TValue>.

OregonGhost
fuente
3

Dependiendo de lo que desee lograr, es posible que desee probar KeyValuePair .

El hecho de que no pueda cambiar la clave de una entrada puede, por supuesto, rectificarse simplemente reemplazando la entrada completa por una nueva instancia de KeyValuePair.

Grimtron
fuente
2

Estaba haciendo la misma pregunta justo ahora, después de un rápido google, descubrí que hay una clase de pares en .NET, excepto que está en System.Web.UI ^ ~ ^ (http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx ) Dios sabe por qué lo pusieron allí en lugar del marco de colecciones


fuente
Sé sobre System.Web.UI.Pair. Aunque quería clase genérica.
Alexander Prokofyev
System.Web.UI.Pair está sellado. No puede derivar de él (en caso de que desee agregar accesores seguros de tipo).
Martin Vobr
2

Desde .NET 4.0 tienes System.Tuple<T1, T2>clase:

// pair is implicitly typed local variable (method scope)
var pair = System.Tuple.Create("Current century", 21);
Serge Mikhailov
fuente
@Alexander, puedes ver fácilmente documentos de .NET 3.5 en Tuple
Serge Mikhailov
En la parte inferior dicen: Información sobre la versión de NET Framework Compatible con: 4
Alexander Prokofyev
2
@Alexander: OK, cierto. (Aunque me dejó preguntándome por qué hicieron esta página .NET 3.5 específica)
Serge Mikhailov
2

Normalmente extiendo la Tupleclase a mi propio contenedor genérico de la siguiente manera:

public class Statistic<T> : Tuple<string, T>
{
    public Statistic(string name, T value) : base(name, value) { }
    public string Name { get { return this.Item1; } }
    public T Value { get { return this.Item2; } }
}

y úsalo así:

public class StatSummary{
      public Statistic<double> NetProfit { get; set; }
      public Statistic<int> NumberOfTrades { get; set; }

      public StatSummary(double totalNetProfit, int numberOfTrades)
      {
          this.TotalNetProfit = new Statistic<double>("Total Net Profit", totalNetProfit);
          this.NumberOfTrades = new Statistic<int>("Number of Trades", numberOfTrades);
      }
}

StatSummary summary = new StatSummary(750.50, 30);
Console.WriteLine("Name: " + summary.NetProfit.Name + "    Value: " + summary.NetProfit.Value);
Console.WriteLine("Name: " + summary.NumberOfTrades.Value + "    Value: " + summary.NumberOfTrades.Value);
parlamento
fuente
1

Para que funcione lo anterior (necesitaba un par como clave de un diccionario). Tuve que agregar:

    public override Boolean Equals(Object o)
    {
        Pair<T, U> that = o as Pair<T, U>;
        if (that == null)
            return false;
        else
            return this.First.Equals(that.First) && this.Second.Equals(that.Second);
    }

y una vez que hice eso también agregué

    public override Int32 GetHashCode()
    {
        return First.GetHashCode() ^ Second.GetHashCode();
    }

para suprimir una advertencia del compilador.

Andrew Stein
fuente
1
Debería encontrar un algoritmo de código hash mejor que ese, intente usar 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...))) Esto hará que (A, B) sea distinto de (B, A ), es decir. La reordenación tendrá un efecto en el código.
Lasse V. Karlsen
Comentario Se aceptó un .. En mi caso, yo sólo estaba tratando de suprimir el compilador menguante, y de todos modos T es una cadena y un Int32 T ...
Andrew Stein
1

Además de la clase personalizada o .Net 4.0 Tuples, desde C # 7.0 hay una nueva característica llamada ValueTuple, que es una estructura que se puede utilizar en este caso. En lugar de escribir:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);

y acceder a los valores a través de t.Item1y t.Item2, simplemente puede hacerlo así:

(string message, int count) = ("Hello", 4);

o incluso:

(var message, var count) = ("Hello", 4);
Pawel Gradecki
fuente