Was ist C # analog zu C ++ std :: pair?


284

Ich bin interessiert: Wovon ist C # std::pairin C ++ analog ? Ich habe System.Web.UI.PairKlasse gefunden , aber ich würde etwas vorlagenbasiertes bevorzugen.

Danke dir!


11
Ich hatte vor einiger Zeit die gleiche Anfrage, aber je mehr ich darüber nachdachte, desto mehr möchten Sie vielleicht Ihre eigene Paarungsklasse mit expliziten Klassentypen und Feldern anstelle der generischen "Ersten" und "Zweiten" rollen. Dadurch wird Ihr Code besser lesbar. Eine Pairing-Klasse kann nur 4 Zeilen umfassen. Sie sparen also nicht viel, wenn Sie eine generische Pair <T, U> -Klasse wiederverwenden, und Ihr Code ist besser lesbar.
Mark Lakata

Antworten:


325

Tupel sind seit .NET4.0 verfügbar und unterstützen Generika:

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

In früheren Versionen können Sie System.Collections.Generic.KeyValuePair<K, V>eine Lösung wie die folgende verwenden:

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

Und benutze es so:

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

Dies gibt aus:

test
2

Oder sogar diese verketteten Paare:

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

Das gibt aus:

test
12
true

Siehe meinen Beitrag über das Hinzufügen einer Equals-Methode
Andrew Stein

Tupel <> ist jetzt eine bessere Lösung.
Dkantowitz

6
Da Typparameter, die zur generischen Klasse gehören, in einem Objekterstellungsausdruck (Konstruktoraufruf) nicht abgeleitet werden können, haben die Autoren von BCL eine nicht generische Hilfsklasse namens erstellt Tuple. Daher kann man sagen, Tuple.Create("Hello", 4)was etwas einfacher ist als new Tuple<string, int>("Hello", 4). (Übrigens ist .NET4.0 bereits seit 2010 hier.)
Jeppe Stig Nielsen

4
Wohlgemerkt Tuple<>implementiert solide Equalsund GetHashCodemit Wertesemantik, was großartig ist. Denken Sie daran, wenn Sie Ihre eigenen Tupel implementieren.
Nawfal

Dies ist offensichtlich wegen Equals und GetHashCode
Julx

90

System.Web.UIenthielt die PairKlasse, da sie in ASP.NET 1.1 häufig als interne ViewState-Struktur verwendet wurde.

Update August 2017: C # 7.0 / .NET Framework 4.7 bietet eine Syntax zum Deklarieren eines Tupels mit benannten Elementen mithilfe der System.ValueTupleStruktur.

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

Weitere Syntaxbeispiele finden Sie unter MSDN .

Update Jun 2012: Tuples Seit Version 4.0 Teil von .NET.

Hier ist ein früherer Artikel, der die Aufnahme in.NET4.0 und die Unterstützung von Generika beschreibt:

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

2
Beachten Sie, dass Tupel schreibgeschützt sind. Das heißt, Sie können dies nicht tun:tuple.Item1 = 4;
skybluecodeflier

2
Tupel sind genau das, wonach ich gesucht habe. Vielen Dank.
Gligoran

38

Leider gibt es keine. Sie können das System.Collections.Generic.KeyValuePair<K, V>in vielen Situationen verwenden.

Alternativ können Sie anonyme Typen verwenden, um Tupel zumindest lokal zu verarbeiten:

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

Die letzte Alternative besteht darin, eine eigene Klasse zu erstellen.


2
Um klar zu sein, sind anonyme Typen auch schreibgeschützt - msdn .
bsegraves


11

Einige Antworten scheinen einfach falsch zu sein,

  1. Sie können kein Wörterbuch verwenden, um die Paare (a, b) und (a, c) zu speichern. Das Paarkonzept sollte nicht mit dem assoziativen Nachschlagen von Schlüsseln und Werten verwechselt werden
  2. Ein Großteil des obigen Codes scheint verdächtig zu sein

Hier ist meine Paarklasse

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

Hier ist ein Testcode:

[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
Wenn Sie IEquatable nicht implementieren, erhalten Sie Boxen. Es muss noch mehr Arbeit geleistet werden, um Ihre Klasse korrekt abzuschließen.
Jack

8

Wenn es um Wörterbücher und dergleichen geht, suchen Sie nach System.Collections.Generic.KeyValuePair <TKey, TValue>.


3

Je nachdem , was Sie erreichen möchten, können Sie ausprobieren KeyValuePair .

Die Tatsache, dass Sie den Schlüssel eines Eintrags nicht ändern können, kann natürlich korrigiert werden, indem einfach der gesamte Eintrag durch eine neue Instanz von KeyValuePair ersetzt wird.



2

Ich habe die gleiche Frage gerade nach einer kurzen Google-Suche gestellt. Ich habe festgestellt, dass es in .NET eine Paarklasse gibt, außer in der System.Web.UI ^ ~ ^ (http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx ) Meine Güte weiß, warum sie es dort anstelle des Sammlungsframeworks ablegen


Ich kenne System.Web.UI.Pair. Wollte generische Klasse.
Alexander Prokofyev

System.Web.UI.Pair ist versiegelt. Sie können nicht daraus ableiten (falls Sie sichere Accessoren hinzufügen möchten).
Martin Vobr

2

Seit .NET 4.0 haben Sie System.Tuple<T1, T2>Klasse:

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

@ Alexander, können Sie leicht in .NET 3.5-Dokumente auf Tuple
Serge Mikhailov

Unten heißt es: Versionsinformationen NET Framework Unterstützt in: 4
Alexander Prokofyev

2
@ Alexander: OK, richtig. (Obwohl ich mich gefragt habe, warum sie diese Seite .NET 3.5-spezifisch gemacht haben)
Serge Mikhailov

2

Normalerweise erweitere ich die TupleKlasse wie folgt in meinen eigenen generischen Wrapper:

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

und benutze es so:

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

Damit das oben genannte funktioniert (ich brauchte ein Paar als Schlüssel für ein Wörterbuch). Ich musste hinzufügen:

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

und als ich das getan habe, habe ich auch hinzugefügt

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

um eine Compilerwarnung zu unterdrücken.


1
Sie sollten einen besseren Hash-Code-Algorithmus als diesen finden. Versuchen Sie, 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...)) zu verwenden. Dadurch wird (A, B) von (B, A) unterschieden ), dh. Eine Neuordnung wirkt sich auf den Code aus.
Lasse V. Karlsen

Kommentar wird akzeptiert. In meinem Fall habe ich nur versucht, das Abnehmen des Compilers zu unterdrücken, und trotzdem ist T ein String und U ein Int32 ...
Andrew Stein


1

Abgesehen von benutzerdefinierten Klassen- oder .NET 4.0-Tupeln gibt es seit C # 7.0 eine neue Funktion namens ValueTuple, eine Struktur, die in diesem Fall verwendet werden kann. Anstatt zu schreiben:

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

und Zugriff auf Werte durch t.Item1und t.Item2, Sie können es einfach so machen:

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

oder auch:

(var message, var count) = ("Hello", 4);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.