Estruturas versus classs

Estou prestes a criar 100.000 objects no código. Eles são pequenos, apenas com 2 ou 3 propriedades. Vou colocá-los em uma lista genérica e, quando eles estiverem, vou fazer um loop e verificar o valor ae talvez atualizar o valor b .

É mais rápido / melhor criar esses objects como class ou como struct?

EDITAR

uma. As propriedades são tipos de valor (exceto a string que eu acho?)

b. Eles podem (ainda não temos certeza) ter um método de validação

EDIT 2

Eu queria saber: são objects no heap e a pilha processada igualmente pelo coletor de lixo, ou isso funciona diferente?

É mais rápido criar esses objects como class ou como struct?

Você é a única pessoa que pode determinar a resposta para essa pergunta. Experimente as duas formas, meça uma métrica de desempenho significativa, focada no usuário e relevante e, em seguida, você saberá se a alteração tem um efeito significativo sobre usuários reais em cenários relevantes.

Os structs consomem menos memory heap (porque são menores e mais facilmente compactados, não porque estejam “na pilha”). Mas demoram mais para copiar do que uma cópia de referência. Não sei quais são suas métricas de desempenho para uso ou velocidade de memory; Há uma troca aqui e você é a pessoa que sabe o que é.

É melhor criar esses objects como class ou como struct?

Talvez class, talvez struct. Como regra geral: Se o object é:
1. Pequeno
2. Logicamente um valor imutável
3. Há muitos deles
Então eu consideraria fazer isso como uma estrutura. Caso contrário, eu ficaria com um tipo de referência.

Se você precisar alterar algum campo de uma estrutura, geralmente é melhor construir um construtor que retorne toda uma nova estrutura com o campo configurado corretamente. Isso é talvez um pouco mais lento (medir!), Mas logicamente muito mais fácil de raciocinar.

Os objects no heap e na pilha são processados ​​igualmente pelo coletor de lixo?

Não , eles não são os mesmos porque os objects na pilha são as raízes da coleção . O coletor de lixo não precisa perguntar “essa coisa está na pilha viva?” porque a resposta para essa pergunta é sempre “Sim, está na pilha”. (Agora, você não pode confiar nisso para manter um object ativo porque a pilha é um detalhe de implementação. O jitter tem permissão para introduzir otimizações que, por exemplo, registram o que normalmente seria um valor de pilha, e então ele nunca fica na pilha Portanto, o CG não sabe que ainda está vivo, pois um object registrado pode ter seus descendentes coletados de forma agressiva, assim que o registro que o mantém não será lido novamente.

Mas o coletor de lixo precisa tratar os objects da pilha como vivos, da mesma maneira que trata qualquer object conhecido como vivo. O object na pilha pode se referir a objects alocados em heap que precisam ser mantidos vivos, portanto, o GC deve tratar objects de pilha, como objects alocados em heap, para fins de determinação do conjunto ao vivo. Mas obviamente eles não são tratados como “objects vivos” com o propósito de compactar o heap, porque eles não estão no heap em primeiro lugar.

Está claro?

Às vezes, com struct você não precisa chamar o construtor new () e atribuir diretamente os campos, tornando-o muito mais rápido que o usual.

Exemplo:

 Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i].id = i; list[i].is_valid = true; } 

é cerca de 2 a 3 vezes mais rápido que

 Value[] list = new Value[N]; for (int i = 0; i < N; i++) { list[i] = new Value(i, true); } 

onde Value é uma struct com dois campos (id e is_valid).

Por outro lado, os itens precisam ser movidos ou tipos de valor selecionados, toda a cópia vai atrasá-lo. Para obter a resposta exata, suspeito que você tenha que analisar seu código e testá-lo.

Uma lista retornará uma cópia da estrutura. A atualização exigiria removê-lo da lista e adicioná-lo novamente, criando uma nova cópia com novos valores e atribuindo-a ao índice da lista.

Portanto, é melhor usar classs.

Leitura relacionada: Por que as estruturas mutáveis ​​são “malignas”?

As estruturas podem parecer semelhantes às classs, mas existem diferenças importantes que você deve conhecer. Primeiro de tudo, classs são tipos de referência e estruturas são tipos de valor. Usando structs, você pode criar objects que se comportam como os tipos internos e aproveitar os benefícios deles também.

Quando você chama o operador New em uma class, ele será alocado no heap. No entanto, quando você instancia uma estrutura, ela é criada na pilha. Isso trará ganhos de desempenho. Além disso, você não estará lidando com referências a uma instância de uma estrutura como faria com classs. Você estará trabalhando diretamente com a instância struct. Por causa disso, ao passar uma estrutura para um método, ela é passada por valor em vez de como referência.

Mais aqui:

http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx

Matrizes de estruturas são representadas no heap em um bloco contíguo de memory, enquanto uma matriz de objects é representada como um bloco contíguo de referências com os próprios objects em outro lugar no heap, exigindo memory para os objects e para as referências de matriz .

Neste caso, como você está colocando-os em uma List<> (e uma List<> é apoiada em uma matriz), seria mais eficiente, em termos de memory, usar structs.

(Esteja ciente, porém, que grandes matrizes encontrarão seu caminho no Large Object Heap onde, se sua vida útil for longa, pode ter um efeito adverso no gerenciamento de memory do seu processo. Lembre-se, também, que a memory não é a única consideração.)

Se eles tiverem semântica de valor, provavelmente você deve usar uma estrutura. Se eles tiverem semântica de referência, provavelmente você deveria usar uma class. Há exceções, que tendem a criar uma class mesmo quando há semântica de valor, mas começam a partir daí.

Quanto à segunda edição, o GC lida apenas com o heap, mas há muito mais espaço de pilha do que espaço de pilha, portanto, colocar as coisas na pilha nem sempre é uma vitória. Além disso, uma lista de tipos de estruturas e uma lista de tipos de classs estarão no heap de qualquer forma, portanto, isso é irrelevante neste caso.

Editar:

Estou começando a considerar o termo mal prejudicial. Afinal de contas, fazer uma class mutável é uma má ideia se não for ativamente necessária, e eu não descartaria o uso de uma estrutura mutável. É uma idéia ruim, muitas vezes, quase sempre ser uma má idéia, mas na maioria das vezes não coincide com a semântica de valor, então não faz sentido usar uma struct no caso dado.

Pode haver exceções razoáveis ​​com estruturas aninhadas privadas, onde todos os usos dessa estrutura são, portanto, restritos a um escopo muito limitado. Isso não se aplica aqui.

Realmente, eu acho que “muta-se, por isso é um mau desempenho” não é muito melhor do que continuar com o heap e a pilha (o que, pelo menos, tem algum impacto no desempenho, mesmo que frequentemente seja deturpado). “Ele muta, então é bem provável que não faça sentido considerá-lo como tendo semântica de valor, então é uma estrutura ruim” é apenas um pouco diferente, mas importante, eu acho.

A melhor solução é medir, medir novamente e depois medir um pouco mais. Pode haver detalhes do que você está fazendo, o que pode dificultar uma resposta simplificada e fácil, como “usar estruturas” ou “usar classs”.

Uma estrutura é, no fundo, nada mais nem menos que uma agregação de campos. No .NET, é possível que uma estrutura “pretenda” ser um object, e para cada tipo de estrutura. O .NET define implicitamente um tipo de object heap com os mesmos campos e methods que – sendo um object heap – se comportarão como um object. . Uma variável que contém uma referência a tal object de heap (estrutura “em checkbox”) exibirá semântica de referência, mas uma que contenha uma estrutura diretamente é simplesmente uma agregação de variables.

Acredito que grande parte da confusão entre estrutura e class deriva do fato de que as estruturas têm dois casos de uso muito diferentes, que devem ter diretrizes de design muito diferentes, mas as diretrizes da MS não fazem distinção entre elas. Às vezes há uma necessidade de algo que se comporta como um object; Nesse caso, as diretrizes do MS são bem razoáveis, embora o “limite de 16 bytes” provavelmente seja mais parecido com 24-32. Às vezes, no entanto, o que é necessário é uma agregação de variables. Uma estrutura usada para essa finalidade deve consistir simplesmente em IEquatable(itsType).Equals campos públicos e, possivelmente, em uma substituição Equals , ToString override e IEquatable(itsType).Equals . Estruturas que são usadas como agregações de campos não são objects e não devem fingir ser. Do ponto de vista da estrutura, o significado de campo deveria ser nada mais ou menos do que “a última coisa escrita neste campo”. Qualquer significado adicional deve ser determinado pelo código do cliente.

Por exemplo, se uma estrutura de agregação de variável tiver os membros Minimum e Maximum , a própria estrutura não deve prometer que Minimum < = Maximum . O código que recebe essa estrutura como um parâmetro deve se comportar como se tivesse passado valores Minimum e Maximum separados. Um requisito de que Minimum não seja maior do que Maximum deve ser considerado como um requisito de que um parâmetro Minimum não seja maior do que um Maximum passado separadamente.

Um padrão útil a ser considerado, às vezes, é ter uma ExposedHolder definida como:

 class ExposedHolder { public T Value; ExposedHolder() { } ExposedHolder(T val) { Value = T; } } 

Se alguém tiver uma List> , onde someStruct é uma estrutura de agregação de variables, pode-se fazer coisas como myList[3].Value.someField += 7; , mas dando myList[3].Value para outro código lhe dará o conteúdo do Value vez de dar-lhe um meio de alterá-lo. Por outro lado, se alguém usasse uma List , seria necessário usar var temp=myList[3]; temp.someField += 7; myList[3] = temp; var temp=myList[3]; temp.someField += 7; myList[3] = temp; . Se alguém usou um tipo de class mutável, expondo o conteúdo de myList[3] ao código externo, seria necessário copiar todos os campos para algum outro object. Se alguém utilizasse um tipo de class imutável, ou uma estrutura de "estilo de object", seria necessário construir uma nova instância que fosse como myList[3] exceto por someField que fosse diferente, e então armazenar essa nova instância na lista.

Uma observação adicional: Se você está armazenando um grande número de coisas semelhantes, pode ser bom armazená-las em matrizes de estruturas possivelmente aninhadas, de preferência tentando manter o tamanho de cada matriz entre 1K e 64K ou mais. Matrizes de estruturas são especiais, em que uma indexação irá produzir uma referência direta a uma estrutura dentro, então pode-se dizer "a [12] .x = 5;". Embora seja possível definir objects do tipo array, o C # não permite que eles compartilhem essa syntax com matrizes.

Do ponto de vista do c ++, concordo que será mais lento modificar as propriedades de uma estrutura em comparação com uma class. Mas eu acho que eles serão mais rápidos para ler devido à estrutura sendo alocada na pilha em vez do heap. A leitura de dados do heap requer mais verificações do que da pilha.

Use classs.

Em uma nota geral. Por que não atualizar o valor b conforme você os cria?

Bem, se você for com struct afterall, então se livrar da string e usar buffer de tamanho ou byte de tamanho fixo.

Isso é re: performance.