Qu'est-ce que l'analogue C # de std :: pair C ++?

284

Je suis intéressé: Quel est l'analogue de C # std::pairen C ++? J'ai trouvé la System.Web.UI.Pairclasse, mais je préfère quelque chose basé sur un modèle.

Je vous remercie!

Alexander Prokofyev
la source
11
J'ai eu la même demande il y a un certain temps, mais plus j'y réfléchissais, plus vous pourriez vouloir rouler votre propre classe d'appariement, avec des types et des champs de classe explicites au lieu des "First" et "Second" génériques. Cela rend votre code plus lisible. Une classe d'appariement peut compter aussi peu que 4 lignes, vous n'économisez donc pas beaucoup en réutilisant une classe Pair <T, U> générique et votre code sera plus lisible.
Mark Lakata

Réponses:

325

Les tuples sont disponibles depuis .NET4.0 et prennent en charge les génériques:

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

Dans les versions précédentes, vous pouvez utiliser System.Collections.Generic.KeyValuePair<K, V>une solution comme celle-ci:

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

Et utilisez-le comme ceci:

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

Cela produit:

test
2

Ou même ces paires enchaînées:

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

Cela génère:

test
12
true
Jorge Ferreira
la source
Voir mon article sur l'ajout d'une méthode Equals
Andrew Stein
Tuple <> est maintenant une meilleure solution.
dkantowitz
6
Étant donné que les paramètres de type appartenant à la classe générique ne peuvent pas être déduits dans une expression de création d'objet (appel constructeur), les auteurs de BCL ont créé une classe auxiliaire non générique appelée Tuple. Par conséquent, vous pouvez dire Tuple.Create("Hello", 4)ce qui est un peu plus facile que new Tuple<string, int>("Hello", 4). (Au fait, .NET4.0 est déjà là depuis 2010.)
Jeppe Stig Nielsen
4
Attention, vous Tuple<>implémentez une sémantique solide Equalset GetHashCodede valeur, ce qui est génial. Gardez à l'esprit lorsque vous implémentez vos propres tuples.
nawfal
Ceci est évidemment cassé à cause de Equals et GetHashCode
julx
90

System.Web.UIcontenait la Pairclasse car elle était largement utilisée dans ASP.NET 1.1 en tant que structure ViewState interne.

Mise à jour d'août 2017: C # 7.0 / .NET Framework 4.7 fournit une syntaxe pour déclarer un tuple avec des éléments nommés à l'aide de la System.ValueTuplestructure.

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

voir MSDN pour plus d'exemples de syntaxe.

Mise à jour de juin 2012: Tuples fait partie de .NET depuis la version 4.0.

Voici un article antérieur décrivant l'inclusion dans.NET4.0 et la prise en charge des génériques:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
Jay Walker
la source
2
Notez que les tuples sont en lecture seule. Autrement dit, vous ne pouvez pas faire cela:tuple.Item1 = 4;
skybluecodeflier
2
Les tuples sont exactement ce que je cherchais. Merci.
gligoran
38

Malheureusement, il n'y en a pas. Vous pouvez utiliser le System.Collections.Generic.KeyValuePair<K, V>dans de nombreuses situations.

Alternativement, vous pouvez utiliser des types anonymes pour gérer les tuples, au moins localement:

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

La dernière alternative consiste à créer une propre classe.

Konrad Rudolph
la source
2
Juste pour être clair, les types anonymes sont également en lecture seule - msdn .
bsegraves
21

C # a des tuples à partir de la version 4.0.

Kenan EK
la source
11

Certaines réponses semblent fausses,

  1. vous ne pouvez pas utiliser le dictionnaire pour stocker les paires (a, b) et (a, c). Le concept de paires ne doit pas être confondu avec la recherche associative des clés et des valeurs
  2. beaucoup du code ci-dessus semble suspect

Voici ma classe de paire

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

Voici un code de test:

[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));
    }
}
Antony
la source
3
Si vous n'implémentez pas IEquatable, vous obtiendrez de la boxe. Il y a encore du travail à faire pour terminer correctement votre classe.
Jack
8

S'il s'agit de dictionnaires et similaires, vous recherchez System.Collections.Generic.KeyValuePair <TKey, TValue>.

OregonGhost
la source
3

Selon ce que vous souhaitez accomplir, vous pouvez essayer KeyValuePair .

Le fait que vous ne pouvez pas modifier la clé d'une entrée peut bien sûr être corrigé en remplaçant simplement l'entrée entière par une nouvelle instance de KeyValuePair.

Grimtron
la source
3

J'ai créé une implémentation C # de Tuples, qui résout le problème de manière générique entre deux et cinq valeurs - voici le billet de blog , qui contient un lien vers la source.

Erik Forbes
la source
2

Je posais la même question tout à l'heure après un rapide google, j'ai trouvé qu'il y a une classe de paires dans .NET, sauf dans System.Web.UI ^ ~ ^ (http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx ) Dieu sait pourquoi ils l'ont mis là au lieu du cadre des collections


la source
Je connais System.Web.UI.Pair. Je voulais une classe générique.
Alexander Prokofyev,
System.Web.UI.Pair est scellé. Vous ne pouvez pas en dériver (au cas où vous voudriez ajouter des accesseurs sécurisés de type).
Martin Vobr
2

Depuis .NET 4.0, vous avez une System.Tuple<T1, T2>classe:

// pair is implicitly typed local variable (method scope)
var pair = System.Tuple.Create("Current century", 21);
Serge Mikhailov
la source
@Alexander, vous pouvez facilement consulter les documents .NET 3.5 sur Tuple
Serge Mikhailov
En bas, ils disent: Version Information NET Framework Pris en charge dans: 4
Alexander Prokofyev
2
@Alexander: OK, c'est ça. (Bien que cela me laisse me demander pourquoi ils ont fait cette page spécifique à .NET 3.5)
Serge Mikhailov
2

J'étends généralement la Tupleclasse dans mon propre wrapper générique comme suit:

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

et l'utiliser comme ça:

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);
parlement
la source
1

Afin de faire fonctionner ce qui précède (j'avais besoin d'une paire comme clé d'un dictionnaire). J'ai dû ajouter:

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

et une fois que j'ai fait ça, j'ai aussi ajouté

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

pour supprimer un avertissement du compilateur.

Andrew Stein
la source
1
Vous devriez trouver un meilleur algorithme de code de hachage que cela, essayez d'utiliser 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...))) Cela rendra (A, B) distinct de (B, A ), c'est à dire. la réorganisation aura un effet sur le code.
Lasse V. Karlsen
Le commentaire est accepté .. Dans mon cas, j'essayais simplement de supprimer le déclin du compilateur, et de toute façon T est une chaîne et U un Int32 ...
Andrew Stein
1

Outre la classe personnalisée ou les tuples .Net 4.0, depuis C # 7.0, il existe une nouvelle fonctionnalité appelée ValueTuple, qui est une structure qui peut être utilisée dans ce cas. Au lieu d'écrire:

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

et accéder aux valeurs via t.Item1et t.Item2, vous pouvez simplement le faire comme ça:

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

ou même:

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