2008-10-03 8 views
243

Me interesa lo que es el análogo de C# de C++ std :: pair? He encontrado la clase System.Web.UI.Pair, pero quería algo basado en la plantilla.¿Qué es el análogo de C# de C++ std :: pair?

¡Gracias!

+10

que tenían la misma petición de un rato Hace tiempo, pero cuanto más lo pensaba, es posible que desee simplemente rodar su propia clase de emparejamiento, con tipos de clases y campos explícitos en lugar del genérico "Primero" y "Segundo". Hace que tu código sea más legible. Una clase de emparejamiento puede ser tan pequeña como 4 líneas, por lo que no está ahorrando mucho reutilizando una clase genérica Pair y su código será más legible. –

Respuesta

266

tuplas are available since .NET4.0 y de apoyo genéricos:

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

En versiones anteriores se pueden utilizar System.Collections.Generic.KeyValuePair<K, V> o una solución similar a 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 utilizarlo como esto:

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

Este salidas:

test 
2 

O incluso esta pares encadenados:

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); 

que da salida:

test 
12 
true 
+0

Consulte mi publicación sobre cómo agregar un método Equals –

+0

Tuple <> ahora es una solución mejor. – dkantowitz

+5

Como los parámetros de tipo que pertenecen a la clase genérica no se pueden inferir en una expresión de creación de objeto (llamada de constructor), los autores de BCL crearon una clase de ayuda no genérica llamada 'Tuple'. Por lo tanto, puede decir 'Tuple.Create (" Hello ", 4)' que es un poco más fácil que 'new Tuple (" Hello ", 4)'. (Por cierto, .NET4.0 ya está aquí desde el 2010.) –

6

Si se trata de diccionarios y similares, que están buscando para el sistema. Collections.Generic.KeyValuePair < TKey, TValue >.

36

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

Como alternativa, puede utilizar los tipos anónimos para manejar tuplas, al menos localmente:

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

La última alternativa es crear una clase propia.

+2

Para que quede claro, los tipos anónimos también son de solo lectura: [msdn] (http://msdn.microsoft.com/en-us/library/bb397696.aspx). – bsegraves

3

Según lo que desee lograr, es posible que desee probar KeyValuePair.

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

2

que estaba pidiendo la misma pregunta hace un momento después de una rápida google he encontrado que hay una clase de par en .NET, excepto en el de su System.Web.UI^~^(http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx) Dios sabe por qué lo ponen allí en lugar del marco de colecciones

+0

Sé sobre System.Web.UI.Pair. Aunque quería clase genérica. –

+0

System.Web.UI.Pair está sellado. No puede derivar de esto (en caso de que quiera agregar tipos de acceso seguros). –

3

Creé una implementación C# de Tuples, que resuelve el problema genéricamente para entre dos y cinco valores - here's the blog post, que contiene un enlace a la fuente.

76

System.Web.UI contenía la clase Pair porque se usaba mucho en ASP.NET 1.1 como estructura interna de ViewState.

actualización Ago 2017 C# 7.0/.NET Framework 4.7 proporciona una sintaxis para declarar una tupla con elementos con nombre utilizando el struct System.ValueTuple.

//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 ver más ejemplos de sintaxis.

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

Aquí es an earlier article describing inclusion in.NET4.0 y el apoyo a los genéricos:

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

Ese enlace es una gran lectura. –

+2

Tenga en cuenta que las tuplas son de solo lectura. Es decir, no puede hacer esto: 'tuple.Item1 = 4;' – skybluecodeflier

+2

Tuplas es exactamente lo que estaba buscando. Gracias. – gligoran

20

C# tiene tuples partir de la versión 4.0.

+1

Clase Tuple: http://msdn.microsoft.com/en-us/library/system.tuple.aspx – skst

1

Para que funcione lo anterior (necesitaba un par como la clave de un diccionario). He tenido que añadir:

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 lo hice También he añadido

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

para suprimir una advertencia del compilador.

+1

Debe encontrar un mejor algoritmo de código hash que eso, intente utilizar 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...))) Esto hará que (A, B) sea distinto de (B, A), es decir. reordenar tendrá un efecto en el código. –

+0

El comentario es aceptado. En mi caso, solo estaba tratando de suprimir el compilador disminuyendo, y de todos modos T es un String y U an Int32 ... –

1

La biblioteca PowerCollections (anteriormente disponible de Wintellect pero ahora alojada en Codeplex @http://powercollections.codeplex.com) tiene una estructura de par genérica.

11

Algunas respuestas parecen simplemente incorrecto,

  1. no se puede utilizar el diccionario de cómo almacenaría los pares (a, b) y (a, c). Pares concepto no debe confundirse con asociativa mirada de clave y valores
  2. gran parte del código anterior parece sospechoso

Aquí es mi clase de par

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)); 
    } 
} 
+2

Si no implementa IEquatable, obtendrá el boxeo. Hay más trabajo por hacer para completar su clase correctamente. – Jack

2

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

// pair is implicitly typed local variable (method scope) 
var pair = System.Tuple.Create("Current century", 21); 
+0

Desde .NET 4.0 en realidad –

+0

@Alexander, puede ver fácilmente [documentos de .NET 3.5 en Tuple] (http://msdn.microsoft.com/en-us/library/system.tuple (VS.90) .aspx) –

+0

En la parte inferior se dice: ** Información de la versión ** * NET Framework * Compatible con: 4 –

2

Me suelen extender la clase Tuple en mi propio envoltorio 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 lo utilizan de esta manera:

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); 
1

Aparte de clase personalizada o .Net 4.0 tuplas, ya C# 7.0 hay una nueva característica llamada ValueTuple, que es una estructura que se puede usar 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.Item1 y t.Item2, sólo tiene que hacerlo así:

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

o incluso:

(var message, var count) = ("Hello", 4); 
Cuestiones relacionadas