Cos'è l'analogo C # di C ++ std :: pair?


284

Sono interessato: qual è l'analogo di C # std::pairin C ++? Ho trovato System.Web.UI.Pairlezione, ma preferirei qualcosa basato su template.

Grazie!


11
Ho avuto la stessa richiesta un po 'di tempo fa, ma più ci ho pensato, potresti voler semplicemente ruotare la tua classe di accoppiamento, con tipi e campi di classe espliciti anziché i generici "Primo" e "Secondo". Rende il tuo codice più leggibile. Una classe di accoppiamento può avere un minimo di 4 righe, quindi non stai risparmiando molto riutilizzando una classe Pair <T, U> generica e il tuo codice sarà più leggibile.
Mark Lakata,

Risposte:


325

Le tuple sono disponibili da .NET 4.0 e supportano generici:

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

Nelle versioni precedenti è possibile utilizzare System.Collections.Generic.KeyValuePair<K, V>o una soluzione come la seguente:

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

E usalo in questo modo:

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

Questo produce:

test
2

O anche questa coppia incatenata:

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

Che produce:

test
12
true

Vedi il mio post sull'aggiunta di un metodo uguale
Andrew Stein,

Tuple <> è ora una soluzione migliore.
dkantowitz,

6
Poiché i parametri di tipo appartenenti alla classe generica non possono essere dedotti in un'espressione di creazione di oggetti (chiamata del costruttore), gli autori di BCL hanno creato una classe helper non generica chiamata Tuple. Quindi puoi dire Tuple.Create("Hello", 4)quale è un po 'più facile di new Tuple<string, int>("Hello", 4). (A proposito, .NET4.0 è già qui dal 2010.)
Jeppe Stig Nielsen,

4
Intendiamoci, Tuple<>implementa una semantica solida Equalse GetHashCodedi valore, il che è fantastico. Tenere presente quando si implementano le proprie tuple.
nawfal,

Questo è ovviamente rotto a causa di Equals e GetHashCode
julx

90

System.Web.UIconteneva la Pairclasse perché era ampiamente utilizzata in ASP.NET 1.1 come struttura ViewState interna.

Aggiornamento agosto 2017: C # 7.0 / .NET Framework 4.7 fornisce una sintassi per dichiarare una Tupla con elementi denominati utilizzando la System.ValueTuplestruttura.

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

vedi MSDN per altri esempi di sintassi.

Aggiornamento giugno 2012: Tuples fa parte di .NET dalla versione 4.0.

Ecco un articolo precedente che descrive l'inclusione in.NET4.0 e il supporto per i generici:

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

2
Nota che le Tuple sono di sola lettura. Cioè, non puoi farlo:tuple.Item1 = 4;
skybluecodeflier,

2
Le tuple sono esattamente quello che stavo cercando. Grazie.
Gligoran,

38

Sfortunatamente non ce n'è. È possibile utilizzare il System.Collections.Generic.KeyValuePair<K, V>in molte situazioni.

In alternativa, puoi usare tipi anonimi per gestire le tuple, almeno localmente:

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

L'ultima alternativa è creare una propria classe.


2
Per essere chiari, anche i tipi anonimi sono di sola lettura - msdn .
bsegraves


11

Alcune risposte sembrano sbagliate,

  1. non puoi usare il dizionario come memorizzerebbe le coppie (a, b) e (a, c). Il concetto di coppia non deve essere confuso con la ricerca associativa di chiave e valori
  2. gran parte del codice sopra sembra sospetto

Ecco la mia classe di coppia

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

Ecco un po 'di codice di prova:

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

3
Se non implementi IEquatable otterrai boxe. C'è altro lavoro da fare per completare correttamente la lezione.
Jack,

8

Se si tratta di dizionari e simili, stai cercando System.Collections.Generic.KeyValuePair <TKey, TValue>.


3

A seconda di ciò che vuoi realizzare, potresti provare KeyValuePair .

Il fatto che non sia possibile modificare la chiave di una voce può ovviamente essere corretto semplicemente sostituendo l'intera voce con una nuova istanza di KeyValuePair.



2

Stavo ponendo la stessa domanda subito dopo un veloce google ho scoperto che esiste una classe pair in .NET tranne che in System.Web.UI ^ ~ ^ (http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx ) bontà sa perché l'hanno messo lì invece del framework delle collezioni


Conosco System.Web.UI.Pair. Volevo una classe generica però.
Alexander Prokofyev,

System.Web.UI.Pair è sigillato. Non puoi derivarne (nel caso in cui desideri aggiungere tipi di accessi sicuri).
Martin Vobr,

2

Da .NET 4.0 hai System.Tuple<T1, T2>classe:

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

@Alexander, puoi facilmente consultare i documenti .NET 3.5 su Tuple
Serge Mikhailov,

In fondo si dice: Informazioni sulla versione NET Framework Supportato in: 4
Alexander Prokofyev

2
@Alexander: OK, giusto. (Anche se mi ha lasciato chiedermi perché hanno creato questa pagina specifica per .NET 3.5)
Serge Mikhailov

2

In genere estendo la Tupleclasse nel mio wrapper generico come segue:

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

e usalo così:

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

Per far funzionare quanto sopra (avevo bisogno di un paio come chiave di un dizionario). Ho dovuto aggiungere:

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

e una volta che l'ho fatto ho anche aggiunto

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

per eliminare un avviso del compilatore.


1
Dovresti trovare un algoritmo di codice hash migliore di quello, prova a usare 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...))) Ciò renderà (A, B) distinto da (B, A ), vale a dire. il riordino avrà un effetto sul codice.
Lasse V. Karlsen,

Il commento è accettato .. Nel mio caso stavo solo cercando di sopprimere il compilatore in declino, e comunque T è una stringa e U un Int32 ...
Andrew Stein,


1

Oltre alla classe personalizzata o alle tuple .Net 4.0, dal C # 7.0 esiste una nuova funzionalità chiamata ValueTuple, che è una struttura che può essere utilizzata in questo caso. Invece di scrivere:

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

e accedi ai valori tramite t.Item1e t.Item2, puoi semplicemente farlo in questo modo:

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

o anche:

(var message, var count) = ("Hello", 4);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.