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!


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

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

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.


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


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

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>.


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.



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


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

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

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.


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

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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.