Pergunta

Eu me encontrar, ocasionalmente, no C# 3.0 procurando maneiras de simular a noção de que uma tupla.Ao longo do tempo eu tive várias "homem pobre" implementações, aqui estão algumas delas:

Objeto Básico Matriz:

object[] poorTuple = new object[]{foo,bar,baz}; // basic object array

Mais Fortemente Tipada, HoHoHo...

KeyValuePair<TypeA, KeyValuePair<TypeB, TypeC>> poorTuple;

A implementação de uma classe que pode usar o tipo de inferência (levantada a partir de Programação funcional para o Mundo Real)

public static class Tuple{
  public static Tuple<T1, T2> Create<T1 foo, T2 bar>{
    return new Tuple<T1, T2>(foo, bar);
  }
}
// later: 
var data = Tuple.Create("foo", 42);

Perguntas:

  1. Quaisquer outras formas de ter um homem pobre tupla no C# 3.0 (ou idioma de sua escolha que não tem a estrutura de dados).

  2. O que é o melhor de forma a obter uma tupla no C# 3.0 - se alguém tem uma biblioteca recomendação é bem-vindo.

  3. Em que ponto (sim, generalizar para mim) faz sentido criar um tipo específico, ao invés de incluir algo como uma lista ou tupla?(olhando para as regras de polegar)

Foi útil?

Solução

Você pode criar tipos anônimos que funcionam de maneira semelhante às tuplas, exceto com nomes úteis:

var itemsWithChildCounts 
    = myObjects.Select(x => new { Name = x.Name, Count = x.Children.Count() });

Outras dicas

Aqui está o código para uma tupla genérica retirada de Bill Wagner's artigo Na edição de abril de 2007 de Visual Studio Magazine.

public struct Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
{
    private readonly T1 first;
    public T1 First
    {
        get { return first; }
    }

    private readonly T2 second;
    public T2 Second
    {
        get { return second; }
    } 

    public Tuple(T1 f, T2 s)
    {
        first = f;
        second = s;
    }

    #region IEquatable<Tuple<T1,T2>> Members
    public bool Equals(Tuple<T1, T2> other)
    {
        return first.Equals(other.first) && 
            second.Equals(other.second);
    }

    public override bool Equals(object obj)
    {
        if (obj is Tuple<T1, T2>)
            return this.Equals((Tuple<T1, T2>)obj);
        else
            return false;
    }

    public override int GetHashCode()
    {
        return first.GetHashCode() ˆ second.GetHashCode();
    }
    #endregion
}

Para 1 - 2: prefiro implementar sua própria classe de tupla. A implementação que você roubou é decente. Deve funcionar bem.

Para 3: Aqui está minha regra prática - assim que você reutilizar essa funcionalidade em vários métodos (com os mesmos tipos com o mesmo significado), ou se você a usar em qualquer API pública, acho que é hora de implementar Uma classe "real" com seus tipos específicos.

  1. A "Implementação de uma classe de" método é tão bom quanto o que vai ser (e deve ser na próxima versão .LÍQUIDO de qualquer maneira)
  2. Não faço ideia, eu mantê-los em minha própria biblioteca de classes para agora.
  3. Eu considere a criação de uma classe própria para eles assim que eles ficam expostos ao público, (i.e.quando eles não são usados apenas para armazenar valores relacionados com a classe internamente).

Como você pediu uma opinião, a minha seria sempre criar um tipo-não consigo descobrir um motivo para não fazê-lo.

Na maioria das vezes, você pode descobrir que realmente precisava do tipo (o uso principal é armazenar dois itens em uma coleção ou devolver dois itens de uma chamada de método-em ambos os casos se os itens não estiverem intimamente relacionados, você ' provavelmente está fazendo algo errado).

Eu acho que é bom para criar um novo tipo quando faz sentido para introduzir um novo valor definido no seu programa.Por exemplo, se você estiver escrevendo um complexo calculadora, em seguida, fazer um complexo de tipo de número.Se você realmente quer apenas "cola" de algumas variáveis em conjunto por um momento e, em seguida, uma tupla é provavelmente a melhor escolha.Vamos dizer que você tem uma função simples que reúne dois números a partir da consola...você pode fazer algo como:

static void GetNumbers(out int x, out int y) { ... }
...
int x, y;
GetNumbers(out x, out y);

Eu acho que ele normalmente faz sentido quando um "getter" função tem um valor de retorno, mas neste caso não, porque eu realmente não pode ter dois valores de retorno.Eu poderia ir e fazer um novo tipo chamado TwoNumbers e usar isso, mas eu acho isso rapidamente torna-se mais um problema do que uma solução.Se C# tinha tuplas que eu possa ser capaz de fazer algo como o seguinte:

static (int, int) GetNumbers() { ... }
...
int x, y;
(x, y) = GetNumbers();

Agora, a questão interessante é:embora o C# não tem esse recurso eu sou capaz de implementar-me com uma biblioteca?O código que você sugere é um início, mas não me permite atribuir, como fiz no segundo exemplo.Eu não tenho certeza sobre C#, mas você pode ficar muito danado perto isso em C++, usando o fato de que uma chamada de função pode ser o operando esquerdo do operador de atribuição, quando o valor de retorno é um tipo de referência.Considere o seguinte:

// this class is implemented much like yours only with C++
template<typename T1, typename T2> class tuple { ... }
...
template<typename T1, typename T2> tuple<T1, T2>& tie(T1 a, T2 b) { ... }
...
template<typename T1, typename T2> tuple<T1, T2> get_numbers() { ... }
...
int x, y;
tie(x, y) = get_numbers();

Eu diria que é muito perto...em vez de (x, y) = nós temos tie(x, y) =.Se você está interessado em detalhes de implementação, vou encaminhá-lo para o TR1 biblioteca onde eu aprendi sobre isso:

http://www.boost.org/doc/libs/1_41_0/libs/tuple/doc/tuple_users_guide.html#tiers

De modo a trazer tudo isso de volta para o C# 3.0...certamente podemos criar um genérico tupla de classe como você mostrou, mas podemos criar uma função como amarrar "descompactar" a tupla em C#?Eu ainda não tentei, parece divertido.Sem algo assim, eu estaria relutante em deixar a C# tupla biblioteca proliferam através do meu código.

Eu não gosto de tipo de inferência em C#, pois é abusado muito por escrita mais curta. Claro que é uma característica muito legal, mas as pessoas abusam de isso, como neste caso.

Nesse caso, eu especificaria o tipo explicitamente para evitar confusões sobre o tipo (ou seja, talvez 42 seja um longo, ou um byte ou um curto).

Então, por que não ter uma classe de tupla simples, que pode ser implementada em apenas algumas linhas. E se você é preguiçoso, pode até escrever alguns métodos de extensão para sua classe de tupla. Isso facilita a vida, mas também mais limpa.

Não veja o ponto de ter uma classe de tupla "sofisticada" em vez da genérica que você apresentou (exceto a inferência do tipo).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top