it-swarm-pt.tech

Quando devo usar uma estrutura em vez de uma classe?

MSDN diz que você deve usar estruturas quando precisar de objetos leves. Existem outros cenários quando uma estrutura é preferível a uma classe?

Algumas pessoas podem ter esquecido isso:

  1. structs podem ter métodos.
  2. structs não podem ser herdadas.

Eu entendo as diferenças técnicas entre structs e classes, eu simplesmente não tenho uma boa noção de when usar uma struct.

292
Esteban Araya

MSDN tem a resposta: Escolhendo entre classes e estruturas .

Basicamente, essa página fornece uma lista de verificação de 4 itens e diz para usar uma classe, a menos que seu tipo atenda a todos os critérios.

Não defina uma estrutura, a menos que o tipo tenha todas as seguintes características:

  • Representa logicamente um valor único, semelhante aos tipos primitivos (inteiro, duplo e assim por diante).
  • Tem um tamanho de instância menor que 16 bytes.
  • É imutável.
  • Não precisará ser encaixotado com freqüência.
291
OwenP

Estou surpreso por não ter lido em nenhuma das respostas anteriores, que considero o aspecto mais crucial:

Eu uso estruturas quando quero um tipo sem identidade. Por exemplo, um ponto 3D:

public struct ThreeDimensionalPoint
{
    public readonly int X, Y, Z;
    public ThreeDimensionalPoint(int x, int y, int z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override string ToString()
    {
        return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";
    }

    public override int GetHashCode()
    {
        return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ThreeDimensionalPoint))
            return false;
        ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
        return this == other;
    }

    public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
    }

    public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return !(p1 == p2);
    }
}

Se você tem duas instâncias desta estrutura, você não se importa se elas são uma única parte de dados na memória ou duas. Você só se preocupa com o (s) valor (es) que eles possuem.

53
Andrei Rînea

Bill Wagner tem um capítulo sobre isso em seu livro "c # efetivo" ( http://www.Amazon.com/Effective-Specific-Ways-Improve-Your/dp/032124566 ). Ele conclui usando o seguinte princípio:

  1. A principal responsabilidade do armazenamento de dados tipo é?
  2. Sua interface pública é definida inteiramente por propriedades que acessam ou modificam seus membros de dados?
  3. Tem certeza de que seu tipo nunca terá subclasses?
  4. Tem certeza de que seu tipo nunca será tratado polimorficamente?

Se você responder "sim" a todas as 4 perguntas: use uma estrutura. Caso contrário, use uma classe.

27
Bart Gijssens

Use uma estrutura quando você quiser semântica do tipo de valor em vez de tipo de referência. As estruturas são cópia por valor, portanto, tenha cuidado!

Veja também perguntas anteriores, por ex.

Qual é a diferença entre struct e classe no .NET?

15
Simon Steele

Eu usaria estruturas quando:

  1. um objeto deve ser lido somente (toda vez que você passar/atribuir uma estrutura, ele será copiado). Os objetos somente leitura são ótimos quando se trata de processamento multithreaded, pois eles não recebem bloqueio na maioria dos casos.

  2. um objeto é pequeno e de vida curta. Nesse caso, há uma boa chance de que o objeto seja alocado na pilha, o que é muito mais eficiente do que colocá-lo no heap gerenciado. O que é mais a memória alocada pelo objeto será liberada assim que sair de seu escopo. Em outras palavras, é menos trabalho para o Garbage Collector e a memória é usada de forma mais eficiente.

11
Pawel Pabich

Use uma classe se:

  • Sua identidade é importante. Estruturas são copiadas implicitamente quando são passadas por valor para um método.
  • Ele terá uma pegada de memória grande.
  • Seus campos precisam de inicializadores.
  • Você precisa herdar de uma classe base.
  • Você precisa de um comportamento polimórfico;

Use uma estrutura se:

  • Ele atuará como um tipo primitivo (int, long, byte, etc.).
  • Deve ter uma pegada de memória pequena.
  • Você está chamando um método P/Invoke que requer que uma estrutura seja passada por valor.
  • Você precisa reduzir o impacto da coleta de lixo no desempenho do aplicativo.
  • Seus campos precisam ser inicializados apenas para seus valores padrão. Esse valor seria zero para tipos numéricos, falso para tipos booleanos e nulo para tipos de referência.
    • Observe que, nas estruturas do C # 6.0, pode haver um construtor padrão que pode ser usado para inicializar os campos da estrutura para valores não padrão.
  • Você não precisa herdar de uma classe base (diferente de ValueType, da qual todas as estruturas herdam).
  • Você não precisa de comportamento polimórfico.
9
Yashwanth Chowdary Kata

Eu sempre usei uma estrutura quando queria agrupar alguns valores para passar as coisas de volta de uma chamada de método, mas não precisarei usá-la para nada depois de ler esses valores. Apenas como uma maneira de manter as coisas limpas. Eu tendem a ver as coisas em uma estrutura como "descartável" e as coisas em uma classe como mais úteis e "funcionais"

5
Ryan Skarin

Se uma entidade for imutável, a questão de usar uma estrutura ou uma classe geralmente será de desempenho e não de semântica. Em um sistema de 32/64 bits, as referências de classe exigem 4/8 bytes para armazenar, independentemente da quantidade de informações na classe; copiar uma referência de classe exigirá a cópia de 4/8 bytes. Por outro lado, cada distinto instância de classe terá 8/16 bytes de sobrecarga, além das informações que ela contém e o custo de memória das referências a ela. Suponha que alguém deseje uma matriz de 500 entidades, cada uma contendo quatro inteiros de 32 bits. Se a entidade for um tipo de estrutura, a matriz precisará de 8.000 bytes, independentemente de todas as 500 entidades serem todas idênticas, todas diferentes ou estarem entre elas. Se a entidade for um tipo de classe, a matriz de 500 referências levará 4.000 bytes. Se todas essas referências apontarem para objetos diferentes, os objetos exigiriam 24 bytes adicionais cada (12.000 bytes para todos os 500), um total de 16.000 bytes - o dobro do custo de armazenamento de um tipo de estrutura. Por outro lado, do código criou uma instância de objeto e depois copiou uma referência para todos os 500 slots da matriz, o custo total seria 24 bytes para essa instância e 4.000 para a matriz - um total de 4.024 bytes. Uma grande economia. Poucas situações funcionariam tão bem quanto a última, mas em alguns casos pode ser possível copiar algumas referências a espaços de array suficientes para fazer com que esse compartilhamento valha a pena.

Se a entidade é supostamente mutável, a questão de usar uma classe ou estrutura é, de certa forma, mais fácil. Suponha que "Coisa" seja uma estrutura ou classe que tenha um campo inteiro chamado xe um faça o seguinte código:

 Coisa t1, t2; 
 ... 
 T2 = t1; 
 T2.x = 5; 

Alguém quer que a última declaração afete t1.x?

Se Thing for um tipo de classe, t1 e t2 serão equivalentes, o que significa que t1.xe t2.x também serão equivalentes. Assim, a segunda declaração afetará t1.x. Se Thing for um tipo de estrutura, t1 e t2 serão instâncias diferentes, significando que t1.xe t2.x se referirão a inteiros diferentes. Assim, a segunda instrução não afetará t1.x.

Estruturas mutáveis ​​e classes mutáveis ​​têm comportamentos fundamentalmente diferentes, embora o .net tenha algumas peculiaridades em seu tratamento de mutações estruturais. Se alguém quiser um comportamento de tipo de valor (significando que "t2 = t1" copiará os dados de t1 para t2 enquanto deixa t1 e t2 como instâncias distintas), e se for possível lidar com as peculiaridades na manipulação de tipos de valor do .net, uma estrutura. Se alguém quiser semântica do tipo valor, mas as peculiaridades do .net causariam a quebra da semântica do tipo de valor no aplicativo, use uma classe e um resquício.

4
supercat

Além disso, as excelentes respostas acima:

Estruturas são tipos de valor.

Eles nunca podem ser configurados para Nothing.

Definindo uma estrutura = Nothing, irá definir todos os seus valores para seus valores padrão.

3
George Filippakos

quando você realmente não precisa de comportamento, mas precisa de mais estrutura do que uma simples matriz ou dicionário.

Follow up Isto é como eu penso em estruturas em geral. Eu sei que eles podem ter métodos, mas eu gosto de manter essa distinção mental geral.

2
Jim Deville

Como disse @Simon, as estruturas fornecem semânticas de "tipo de valor", portanto, se você precisar de um comportamento semelhante a um tipo de dados interno, use uma estrutura. Como as structs são passadas por cópia, você quer ter certeza de que elas são pequenas em tamanho, cerca de 16 bytes.

2
Scott Dorman

As structs estão na Stack e não na Heap, portanto, elas são thread-safe e devem ser usadas ao implementar o padrão de objeto de transferência, você nunca deseja usar objetos na Heap que eles são voláteis, você deseja usar a Pilha de Chamadas, este é um caso básico para usar uma estrutura Estou surpreso com todas as respostas aqui,

1
Jack

Hmm...

Eu não usaria a coleta de lixo como um argumento para/contra o uso de estruturas versus classes. O heap gerenciado funciona como uma pilha - criar um objeto simplesmente o coloca no topo do heap, que é quase tão rápido quanto a alocação na pilha. Além disso, se um objeto tiver vida curta e não sobreviver a um ciclo de GC, a desalocação será gratuita, pois o GC só funciona com a memória que ainda está acessível. (Search MSDN, há uma série de artigos sobre gerenciamento de memória .net, estou com preguiça de ir Dig para eles).

Na maioria das vezes eu uso uma struct, acabo me chutando por fazer isso, porque depois descobri que ter semântica de referência teria tornado as coisas um pouco mais simples.

De qualquer forma, esses quatro pontos no artigo do MSDN postado acima parece uma boa diretriz.

1
KG