Quando devo usar uma classe de 2 propriedades em uma estrutura pré-criada como um KeyValuePair?

31

Quando você deve colocar o tipo de dados Chave / Valor em sua própria classe, em vez de usar uma estrutura genérica pré-criada, como a KeyValuePairou a Tuple?

Por exemplo, a maioria das ComboBox que eu crie contém um DisplayName e um Value. Esse é o tipo de dados que estou tentando decidir quando colocar uma nova classe e quando usar apenas um KeyValuePair.

Atualmente, estou trabalhando em algo que usa iCalendar, e os dados do usuário selecionado são combinados em um key1=value1;key2=value2;tipo de string. Comecei colocando os dados em um KeyValuePair<string,string>, mas agora estou me perguntando se deveria ser sua própria classe.

No geral, estou interessado em descobrir quais diretrizes são usadas ao decidir usar uma estrutura / classe existente como um KeyValuePairobjeto sobre duas propriedades e em que tipo de situações você usaria um sobre o outro.

Rachel
fonte
3
Que lingua? No Python, não temos esse dilema, porque temos o tupletipo.
S.Lott
3
@ S.Lott - O BCL do .NET possui tuplas desde a v 4.0.
Oded
1
O .NET 4 também possui o tipo de tupla. msdn.microsoft.com/en-us/library/system.tuple.aspx
Dave Nay
1
@Oded Nesse caso, estou criando algo com iCalendare queria objetos para BYDAYe BYSETPOS. Eles aparecem nas caixas de combinação, mas os dados reais são combinados na sequência de regras recorrente, que é um key=value;tipo de sequência
Rachel
4
@ Rachel: Por favor, atualize a questão para ser mais específico. Muitos comentários não são a melhor maneira de esclarecer as coisas.
S.Lott

Respostas:

26

Geralmente, eu usaria um objeto em vez de um KeyValuePair ou Tuple na maioria dos casos. Primeiro, quando você chega seis meses depois para fazer alterações, é muito mais fácil descobrir qual era sua intenção antes, em vez de se perguntar o queTuple t é e por que ela tem esses valores engraçados. Segundo, à medida que as coisas crescem e mudam, você pode facilmente fornecer o comportamento simples dos objetos de transferência de dados, conforme necessário. Precisa ter dois formatos de nome? Fácil, basta adicionar sobrecargas ToString () apropriadas. Precisa implementar alguma interface? Sem problemas. Por fim, existe quase zero de sobrecarga na criação de um objeto simples, especialmente com propriedades automáticas e conclusão de código.

Proteção de bônus: se você deseja impedir que esses objetos poluam seu espaço para nome, criar classes particulares dentro das classes é uma ótima maneira de manter as coisas em sigilo e evitar dependências estranhas.

Wyatt Barnett
fonte
1
DTOs = objetos de transferência de dados ? - Eu odeio TLAs ;-)
Ben
3
@Ben - TFTFY. . .
Wyatt Barnett
7

A regra para definir uma nova classe é simples: é resumida em "Navalha de Occam".

http://c2.com/cgi/wiki?OccamsRazor

Não introduza novas classes sem uma boa razão. Ou use classes pré-criadas o máximo possível. Ou invente o mínimo possível. No entanto, você se sente à vontade para escrever menos código.

Você não pode usar uma classe pré-criada se precisar atribuir alguma responsabilidade exclusiva ao objeto e essa responsabilidade não estiver definida na classe pré-criada. Muitas vezes, este é um método único.

A tupleou KeyValuePairé preferido.

Até.

Você precisa de algumas funcionalidades que não fazem parte de A tupleou KeyValuePair. Então você tem que definir sua própria classe.

S.Lott
fonte
3
" Nunca projete o que você pode roubar " É a minha maneira favorita de expressar isso.
SingleNegationElimination
1
Você consideraria a semântica como "funcionalidade que não faz parte de um tupleou KeyValuePair"? KeyValuePair pelo menos tem alguma forma limitada de semântica, mas as tuplas raramente têm (pode ser possível, dependendo do contexto) imho. A introdução de uma nova classe deve fornecer claramente uma semântica clara.
Mal Ross
+1 Não remende um todo em um barco com fita adesiva, se puder encaixar um plugue.
precisa saber é o seguinte
4
Acho que esse é um uso inadequado da lâmina de Occam. Em segundo lugar, é importante saber quais informações estão contidas em uma variável. -1.
Bent
4

Se você escreve sua própria classe, tem um lugar claro para colocar a documentação sobre quais são os dois valores. Especialmente porque duas strings não são uma definição muito clara. No entanto, você poderia escrever algo como

 public class MyPair : Tuple<string, string>
Placa
fonte
Eu gosto disso porque MyPairdefine quais são os valores da Tupla e para que são usados.
IAbstract
2
Mas então suas propriedades seriam chamadas Item1e Item2! Não é tão fácil definir toda a classe? public class SideStrings { public string Left { get; set; } public string Right { get; set; } }
configurator
@configurator Eu estava escrevendo uma resposta para isso e notei que Tuple é somente leitura, então eu concordo, o que eu acho que fica discutindo a coisa toda. Embora você possa agrupar a tupla para nomear os valores como desejar.
Assine
2
@Sign: Mas qual é o objetivo? Criar sua própria classe é menos trabalhoso do que agrupar uma tupla.
configurator
2
@configurator você obtém algumas coisas de igualdade e comparação que funcionam melhor do que um objeto personalizado, mas se a configuração for necessária, a tupla é definitivamente o caminho errado.
Assine
4

S.Lott escreveu

Não introduza novas classes sem uma boa razão. Ou use classes pré-criadas o máximo possível. Invente o mínimo possível.

Você não pode usar uma classe pré-criada se precisar atribuir uma responsabilidade exclusiva ao objeto que não está definido na classe pré-criada.

Deixe-me dizer por que tenho um problema sério com isso. Desde a

KeyValuePair [string, string]

parece estar bem .... KeyValue [string, KeyValue [string, KeyValuePair [string, string]]] também está bem? Não sei o que S.Lott vai dizer sobre isso, mas meu chefe acha que está tudo bem.

Atrevo-me a discordar. E aqui está o porquê: Reduz a legibilidade do código a seguir. A função que preencherá essa estrutura de dados será muito mais complexa e sujeita a erros (err .. exceção) (imagine também pelo menos alguma lógica de negócios). Não discuti muito com meu chefe, mas vou dizer aqui: A legibilidade supera a economia de espaço em minutos (que era sua argumento ). Portanto, não seria um objeto de classe no qual existem alguns campos; mas alguns permanecem vazios às vezes melhor?

PS Eu sou um Ultra_Noob, por favor, diga-me se estou errado.

Chani
fonte
2
Eu acho que isso KeyValuePair<string,string>é bom, assumindo que as coisas colocadas são de fato chaves e valores. Aqui, reutilizar uma classe existente é uma vitória, pois você salva o código de gravação e obtém interoperabilidade. OTOH, usar algo tão complicado quanto na KeyValuePair<string,KeyValuePair<string,string>>maioria das vezes é complicado demais para ser prático. Às vezes, pode estar certo - quando você não precisa de funcionalidade adicional, quando o significado é óbvio e quando funciona bem com outras classes. YMMV, isso está apenas pesando os prós e contras.
maaartinus 13/09/11
Se você estiver usando C #, aqui está o desempenho de uma Tupla vs KeyValuePair . Algo que você pode querer compartilhar com seu chefe?
Ben
3

Quando dizemos par de chave / valor , geralmente penso em tabelas de hash ou matrizes associativas ou simplesmente em um objeto KeyValuePair . Eu uso todos eles e não há diferença em quando eu uso quais.

Eu acho que a coisa mais importante em uma lista de pares chave / valor é que, chaves devem ser únicas (já que é uma chave, afinal), e todas as estruturas acima mencionadas realmente me fornecem essa funcionalidade.

Fora isso, quero pesquisar por chaves ou executar ações no estilo de lista (matriz) como push e pop.

Portanto, minha resposta é NÃO e não crio objetos explicitamente para pares de chave / valor, pois muitas estruturas internas já fazem isso por mim.

Saeed Neamati
fonte
3

O capítulo seis do Código Limpo tem uma boa descrição de quando usar uma estrutura de dados versus uma classe. Em poucas palavras, você usa uma estrutura de dados quando antecipa a adição de novas funções para operar com esses dados. Você usa uma classe quando antecipa a adição de novos tipos de dados a serem operados pelas funções existentes. Seu ComboBox é um exemplo deste último. Você tem um conjunto de funções (a implementação da ComboBox) operando em vários tipos de dados diferentes (diferentes tipos de dados para diferentes widgets).

Karl Bielefeldt
fonte
2

Talvez eu concorde com Wilding aqui , mas também sou um pouco irritado com esse tipo de coisa.

Eu sugeriria que os tipos internos geralmente são objetos de mecanismo . KeyValuePair, por exemplo, faz parte do mecanismo de um dicionário e tabelas de hash, ajudando a garantir chaves únicas e elas possuem propriedades com nomes significativos no contexto do próprio mecanismo.

Por outro lado, o uso de objetos de dados personalizados fornece uma melhor representação dos seus dados e muitos benefícios, incluindo legibilidade e extensibilidade. Não há nada para parar de reutilizar o código existente em objetos personalizados, como sugerido pelo Sign , mas eu tentaria ocultar a classe agrupada e evitar vazamentos de abstração , para que você possa alterar a implementação subjacente posteriormente, sem afetar o restante do código. .

Então a verdadeira questão: você está representando dados ou está usando os dados em um mecanismo? (e eu não recomendaria misturar esses conceitos).

Na minha experiência, não há nada pior do que ver uma Tupla [string, string] sendo passada para um método e não ter uma maneira imediata de saber o que o Item1 e o Item2 devem ser. É melhor usar Tuplas dentro de métodos ou apenas como membros privados de uma classe.

Ben
fonte