Quais são as suas regras práticas para quando usar estruturas versus classes? Estou pensando na definição de C # desses termos, mas se o seu idioma tiver conceitos semelhantes, também gostaria de ouvir sua opinião.
Costumo usar classes para quase tudo e usar estruturas apenas quando algo é muito simplista e deve ser um tipo de valor, como um PhoneNumber ou algo parecido. Mas isso parece ser um uso relativamente pequeno e espero que haja casos de uso mais interessantes.
Respostas:
A regra geral a seguir é que as estruturas devem ser coleções pequenas e simples (de um nível) de propriedades relacionadas, imutáveis após a criação; para qualquer outra coisa, use uma classe.
C # é bom, pois estruturas e classes não têm diferenças explícitas na declaração além da palavra-chave de definição; portanto, se você sentir que precisa "atualizar" uma estrutura para uma classe ou, inversamente, "fazer o downgrade" de uma classe para uma estrutura, é uma questão simples de alterar a palavra-chave (existem algumas outras dicas; as estruturas não podem derivar de qualquer outra classe ou tipo de estrutura, e eles não podem definir explicitamente um construtor sem parâmetros padrão).
Eu digo "principalmente", porque o mais importante a saber sobre estruturas é que, por serem tipos de valor, tratá-las como classes (tipos de referência) pode acabar meio doloroso. Particularmente, tornar mutáveis as propriedades de uma estrutura pode causar comportamento inesperado.
Por exemplo, digamos que você tenha uma classe SimpleClass com duas propriedades, A e B. Você instancia uma cópia dessa classe, inicializa A e B e passa a instância para outro método. Esse método modifica ainda mais A e B. De volta à função de chamada (a que criou a instância), os A e B da sua instância terão os valores dados a eles pelo método chamado.
Agora, você faz disso uma estrutura. As propriedades ainda são mutáveis. Você executa as mesmas operações com a mesma sintaxe de antes, mas agora os novos valores de A e B não estão na instância depois de chamar o método. O que aconteceu? Bem, sua classe agora é uma estrutura, o que significa que é um tipo de valor. Se você passar um tipo de valor para um método, o padrão (sem uma palavra-chave out ou ref) é passar "por valor"; uma cópia superficial da instância é criada para uso pelo método e, em seguida, destruída quando o método é concluído, deixando a instância inicial intacta.
Isso se torna ainda mais confuso se você tiver um tipo de referência como membro de sua estrutura (não é permitido, mas é uma prática extremamente ruim em praticamente todos os casos); a classe não seria clonada (apenas a referência da estrutura), portanto, as alterações na estrutura não afetariam o objeto original, mas as alterações na subclasse da estrutura afetarão a instância a partir do código de chamada. Isso pode facilmente colocar estruturas mutáveis em estados muito inconsistentes que podem causar erros muito longe de onde está o problema real.
Por esse motivo, praticamente toda autoridade em C # diz sempre tornar suas estruturas imutáveis; permita que o consumidor especifique os valores das propriedades apenas na construção de um objeto e nunca forneça meios para alterar os valores dessa instância. Campos somente leitura, ou propriedades de obtenção apenas, são a regra. Se o consumidor deseja alterar o valor, ele pode criar um novo objeto com base nos valores do antigo, com as alterações que deseja, ou pode chamar um método que fará o mesmo. Isso os obriga a tratar uma única instância de sua estrutura como um "valor" conceitual, indivisível e distinto de (mas possivelmente equivalente a) todos os outros. Se eles executarem uma operação em um "valor" armazenado por seu tipo, obterão um novo "valor" diferente do valor inicial,
Para um bom exemplo, veja o tipo DateTime. Você não pode atribuir nenhum dos campos de uma instância DateTime diretamente; você deve criar um novo ou chamar um método existente, o que produzirá uma nova instância. Isso ocorre porque uma data e hora são um "valor", como o número 5, e uma alteração no número 5 resulta em um novo valor que não é 5. Só porque 5 + 1 = 6 não significa que 5 agora é 6 porque você adicionou 1 a ele. DateTimes funcionam da mesma maneira; 12:00 não "se torna" 12:01 se você adicionar um minuto, em vez disso, obtém um novo valor 12:01 diferente de 12:00. Se esse é um estado lógico para o seu tipo (bons exemplos conceituais que não são incorporados ao .NET são Dinheiro, Distância, Peso e outras quantidades de uma UOM em que as operações devem levar em consideração todas as partes do valor), use uma estrutura e projete-a de acordo. Na maioria dos outros casos em que os subitens de um objeto devem ser mutáveis independentemente, use uma classe.
fonte
A resposta: "Use uma estrutura para construções de dados puros e uma classe para objetos com operações" é definitivamente IMO errado. Se uma estrutura possui um grande número de propriedades, uma classe é quase sempre mais apropriada. A Microsoft costuma dizer que, do ponto de vista da eficiência, se seu tipo for maior que 16 bytes, deve ser uma classe.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
fonte
A diferença mais importante entre a
class
e astruct
é o que acontece na seguinte situação:Qual deve ser o efeito da última declaração
thing1.somePropertyOrField
? SeThing
uma estrutura, esomePropertyOrField
é um campo público exposto, os objetosthing1
ething2
será "isolada" uma da outra, de modo que esta última afirmação não afetaráthing1
. SeThing
é uma classe, entãothing1
ething2
serão anexados um ao outro e, portanto, a última declaração será escritathing1.somePropertyOrField
. Deve-se usar uma estrutura nos casos em que a semântica anterior faria mais sentido, e deveria usar uma classe nos casos em que a semântica anterior faria mais sentido.Observe que, enquanto algumas pessoas aconselham que o desejo de tornar algo mutável é um argumento a favor de ser da classe, eu sugeriria o contrário: se algo que existe com o objetivo de armazenar alguns dados será mutável, e se não for óbvio se as instâncias estão anexadas a alguma coisa, a coisa deve ser uma estrutura (provavelmente com campos expostos), para deixar claro que as instâncias não estão anexadas a qualquer outra coisa.
Por exemplo, considere as instruções:
A segunda instrução alterará as informações armazenadas
myPeople
? SePerson
for uma estrutura de campo exposto, não será, e o fato de não ser será uma conseqüência óbvia de ser uma estrutura de campo exposto ; sePerson
é uma estrutura e se deseja atualizarmyPeople
, seria necessário fazer algo parecidomyPeople.UpdatePerson("123-45-6789", somePerson)
. SePerson
for uma classe, no entanto, pode ser muito mais difícil determinar se o código acima nunca atualiza o conteúdoMyPeople
, sempre o atualiza ou, algumas vezes, atualiza-o.No que diz respeito à noção de que estruturas devem ser "imutáveis", discordo em geral. Existem casos de uso válidos para estruturas "imutáveis" (onde invariantes são impingidos em um construtor), mas exigindo que uma estrutura inteira seja reescrita sempre que qualquer parte dela for alterada, desajeitada e mais propensa a causar bugs do que simplesmente expor campos diretamente. Por exemplo, considere uma
PhoneNumber
estrutura cujos campos incluam entre outrosAreaCode
eExchange
, e suponha que um possua aList<PhoneNumber>
. O efeito do seguinte deve ser bem claro:Observe que nada no código acima sabe ou se importa com nenhum campo
PhoneNumber
além deAreaCode
eExchange
. SePhoneNumber
fosse uma estrutura chamada "imutável", seria necessário fornecer umwithXX
método para cada campo, que retornasse uma nova instância de estrutura que contivesse o valor passado no campo indicado, ou seria necessário para código como o acima saber sobre todos os campos na estrutura. Não é exatamente atraente.BTW, há pelo menos dois casos em que estruturas podem conter razoavelmente referências a tipos mutáveis.
No cenário anterior, a IDENTIDADE do objeto será imutável; no segundo, a classe do objeto aninhado pode não impor a imutabilidade, mas a estrutura que mantém a referência o fará.
fonte
Eu costumo usar estruturas somente quando há um método necessário, fazendo com que uma classe pareça muito "pesada". Assim, às vezes eu trato estruturas como objetos leves. CUIDADO: isso nem sempre funciona e nem sempre é a melhor coisa a fazer, por isso realmente depende da situação!
RECURSOS EXTRA
Para uma explicação mais formal, o MSDN diz: http://msdn.microsoft.com/en-us/library/ms229017.aspx Este link verifica o que mencionei acima, as estruturas devem ser usadas apenas para armazenar uma pequena quantidade de dados.
fonte
Use uma estrutura para construções de dados puros e uma classe para objetos com operações.
Se sua estrutura de dados não precisar de controle de acesso e não possuir operações especiais além de obter / definir, use uma estrutura. Isso torna óbvio que tudo o que essa estrutura é é um contêiner para dados.
Se sua estrutura possui operações que modificam a estrutura de maneira mais complexa, use uma classe Uma classe também pode interagir com outras classes através de argumentos para métodos.
Pense nisso como a diferença entre um tijolo e um avião. Um tijolo é uma estrutura; tem comprimento, largura e altura. Não pode fazer muito. Um avião pode ter várias operações que o modificam de alguma forma, como mover superfícies de controle e alterar a pressão do motor. Seria melhor como classe.
fonte