O que é o C # analógico do C ++ std :: pair?

284

Estou interessado: De que é o análogo do C # std::pairem C ++? Encontrei System.Web.UI.Pairclasse, mas prefiro algo baseado em modelo.

Obrigado!

Alexander Prokofyev
fonte
11
Eu tive o mesmo pedido há um tempo atrás, mas quanto mais eu pensava sobre isso, convém rolar sua própria classe de emparelhamento, com tipos e campos explícitos de classe, em vez dos genéricos "Primeiro" e "Segundo". Isso torna seu código mais legível. Uma classe de emparelhamento pode ter apenas 4 linhas, portanto você não está economizando muito reutilizando uma classe Pair <T, U> genérica e seu código ficará mais legível.
precisa saber é o seguinte

Respostas:

325

As tuplas estão disponíveis desde o .NET4.0 e os genéricos de suporte:

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

Nas versões anteriores, você pode usar System.Collections.Generic.KeyValuePair<K, V>ou uma solução como a seguinte:

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 use-o assim:

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

Isso gera:

test
2

Ou mesmo esses pares encadeados:

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

Isso gera:

test
12
true
Jorge Ferreira
fonte
Veja meu post sobre como adicionar um método Equals
Andrew Stein
Tupla <> agora é uma solução melhor.
Dkantowitz 03/08/12
6
Como os parâmetros de tipo pertencentes à classe genérica não podem ser inferidos em uma expressão de criação de objeto (chamada de construtor), os autores da BCL criaram uma classe auxiliar não genérica chamada Tuple. Portanto, você pode dizer o Tuple.Create("Hello", 4)que é um pouco mais fácil do que new Tuple<string, int>("Hello", 4). (By the way, .NET4.0 já está aqui desde 2010.)
Jeppe Stig Nielsen
4
Lembre-se de que você Tuple<>implementa uma sólida Equalse GetHashCodesemântica de valores, o que é ótimo. Lembre-se de implementar suas próprias tuplas.
Nawfal
Obviamente, isso está quebrado por causa de Equals e GetHashCode #
307 julx
90

System.Web.UIcontinha a Pairclasse porque era muito usada no ASP.NET 1.1 como uma estrutura interna do ViewState.

Atualização em agosto de 2017: O C # 7.0 / .NET Framework 4.7 fornece uma sintaxe para declarar uma Tupla com itens nomeados usando a System.ValueTupleestrutura.

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

consulte MSDN para mais exemplos de sintaxe.

Atualização de junho de 2012: Tuples parte do .NET desde a versão 4.0.

Aqui está um artigo anterior que descreve a inclusão no.NET4.0 e o suporte a genéricos:

Tuple<string, int> t = new Tuple<string, int>("Hello", 4);
Jay Walker
fonte
2
Observe que as tuplas são somente leitura. Ou seja, você não pode fazer isso:tuple.Item1 = 4;
skybluecodeflier
2
Tuplas são exatamente o que eu estava procurando. Obrigado.
gligoran
38

Infelizmente, não há. Você pode usar o System.Collections.Generic.KeyValuePair<K, V>em muitas situações.

Como alternativa, você pode usar tipos anônimos para manipular tuplas, pelo menos localmente:

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

A última alternativa é criar uma própria classe.

Konrad Rudolph
fonte
2
Só para esclarecer, os tipos anônimos também são somente leitura - msdn .
bsegraves
21

C # possui tuplas na versão 4.0.

Kenan EK
fonte
11

Algumas respostas parecem erradas,

  1. você não pode usar o dicionário como armazenaria os pares (a, b) e (a, c). O conceito de pares não deve ser confundido com a pesquisa associativa de chaves e valores
  2. muito do código acima parece suspeito

Aqui está a minha aula 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;
    }
}

Aqui está um código de teste:

[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
fonte
3
Se você não implementar o IEquatable, receberá boxe. Há mais trabalho a ser feito para concluir sua aula corretamente.
Jack
8

Se é sobre dicionários e coisas do gênero, você está procurando System.Collections.Generic.KeyValuePair <TKey, TValue>.

OregonGhost
fonte
3

Dependendo do que você deseja realizar, você pode experimentar o KeyValuePair .

É claro que o fato de você não poder alterar a chave de uma entrada pode ser corrigido simplesmente substituindo a entrada inteira por uma nova instância do KeyValuePair.

Grimtron
fonte
3

Criei uma implementação em C # das Tuplas, que resolve o problema genericamente entre dois e cinco valores - aqui está a postagem do blog , que contém um link para a fonte.

Erik Forbes
fonte
2

Eu estava fazendo a mesma pergunta agora, depois de um rápido google, descobri que existe uma classe de pares no .NET, exceto a que está no System.Web.UI ^ ~ ^ (http://msdn.microsoft.com/en-us/library/system.web.ui.pair.aspx ) Deus sabe por que eles colocaram lá em vez da estrutura de coleções


fonte
Eu sei sobre System.Web.UI.Pair. Queria classe genérica embora.
Alexander Prokofyev
System.Web.UI.Pair está selado. Você não pode derivar disso (caso deseje adicionar acessadores seguros).
Martin Vobr 11/02/10
2

Desde o .NET 4.0 você tem System.Tuple<T1, T2>classe:

// pair is implicitly typed local variable (method scope)
var pair = System.Tuple.Create("Current century", 21);
Serge Mikhailov
fonte
@Alexander, você pode facilmente olhar para .NET 3.5 docs em Tuple
Serge Mikhailov
Na parte inferior, eles dizem: Informações sobre versão NET Framework Compatível com: 4
Alexander Prokofyev
2
@ Alexander: OK, certo. (Embora ele deixou-me perguntando por que eles fazem desta página NET 3.5-específica)
Serge Mikhailov
2

Normalmente, estendo a Tupleclasse para meu próprio invólucro genérico da seguinte maneira:

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 use-o assim:

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
fonte
1

Para que o procedimento acima funcione (eu precisava de um par como chave de um dicionário). Eu tive que adicionar:

    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 uma vez que eu fiz isso também adicionei

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

suprimir um aviso do compilador.

Andrew Stein
fonte
1
Você deve encontrar um algoritmo de código hash melhor do que isso, tente usar 37 + 23 * (h1 + 23 * (h2 + 23 * (h3 + ...)))) Isso fará com que (A, B) seja diferente de (B, A ), ie. reordenar terá um efeito no código.
Lasse V. Karlsen
Comentário é um aceite .. No meu caso eu estava apenas tentando suprimir o compilador minguante, e de qualquer maneira T é uma String e U um Int32 ...
Andrew Stein
1

A biblioteca do PowerCollections (anteriormente disponível na Wintellect, mas agora hospedada no Codeplex @ http://powercollections.codeplex.com ) possui uma estrutura de pares genérica.

James Webster
fonte
1

Além da classe personalizada ou das tuplas .Net 4.0, desde o C # 7.0, há um novo recurso chamado ValueTuple, que é uma estrutura que pode ser usada nesse caso. Em vez de escrever:

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

e acessar valores por meio de t.Item1e t.Item2, você pode simplesmente fazer o seguinte:

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

ou até:

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