Quando você deve usar struct e não classe em c #? Meu modelo conceitual é que as estruturas são usadas nos momentos em que o item é apenas uma coleção de tipos de valor . Uma maneira de uni-los logicamente em um todo coeso.
Encontrei estas regras aqui :
- Uma estrutura deve representar um valor único.
- Uma estrutura deve ter um espaço de memória menor que 16 bytes.
- Uma estrutura não deve ser alterada após a criação.
Essas regras funcionam? O que significa uma estrutura semanticamente?
System.Drawing.Rectangle
viola todas essas três regras.Respostas:
A fonte mencionada pelo OP tem alguma credibilidade ... mas e a Microsoft - qual é a posição sobre o uso da estrutura? Procurei um aprendizado extra da Microsoft e aqui está o que encontrei:
A Microsoft viola consistentemente essas regras
Ok, # 2 e # 3 de qualquer maneira. Nosso amado dicionário possui 2 estruturas internas:
* Fonte de referência
A fonte 'JonnyCantCode.com' obteve 3 em 4 - bastante perdoável, já que o nº 4 provavelmente não seria um problema. Se você estiver no boxe de uma estrutura, repensar sua arquitetura.
Vejamos por que a Microsoft usaria essas estruturas:
Entry
eEnumerator
representa valores únicos.Entry
nunca é passado como um parâmetro fora da classe Dictionary. Investigações adicionais mostram que, para satisfazer a implementação de IEnumerable, o Dictionary usa aEnumerator
estrutura que ele copia toda vez que um enumerador é solicitado ... faz sentido.Enumerator
é público porque Dictionary é enumerável e deve ter acessibilidade igual à implementação da interface IEnumerator - por exemplo, getter IEnumerator.Atualização - Além disso, saiba que quando uma estrutura implementa uma interface - como o Enumerator - e é convertida para esse tipo implementado, a estrutura se torna um tipo de referência e é movida para o heap. Interno à classe Dictionary, o Enumerator ainda é um tipo de valor. No entanto, assim que um método é chamado
GetEnumerator()
, um tipo de referênciaIEnumerator
é retornado.O que não vemos aqui é qualquer tentativa ou prova de exigência de manter estruturas imutáveis ou manter um tamanho de instância de apenas 16 bytes ou menos:
readonly
- não é imutávelEntry
tem um tempo de vida indeterminada (a partir deAdd()
, aRemove()
,Clear()
ou a recolha de lixo);E ... 4. Ambas as estruturas armazenam TKey e TValue, que todos sabemos que são capazes de serem tipos de referência (informações adicionais sobre bônus)
Não obstante as chaves com hash, os dicionários são rápidos em parte porque instanciar uma estrutura é mais rápido que um tipo de referência. Aqui, eu tenho um
Dictionary<int, int>
que armazena 300.000 números aleatórios com chaves incrementadas sequencialmente.Capacidade : número de elementos disponíveis antes que a matriz interna precise ser redimensionada.
MemSize : determinado serializando o dicionário em um MemoryStream e obtendo um comprimento de bytes (preciso o suficiente para nossos propósitos).
Redimensionamento concluído : o tempo necessário para redimensionar a matriz interna de 150862 elementos para 312874 elementos. Quando você descobre que cada elemento é copiado sequencialmente
Array.CopyTo()
, isso não é muito ruim.Tempo total para preencher : reconhecidamente distorcido devido ao log e a um
OnResize
evento que adicionei à fonte; no entanto, ainda é impressionante preencher 300k números inteiros enquanto redimensiona 15 vezes durante a operação. Por curiosidade, qual seria o tempo total a preencher se eu já soubesse a capacidade? 13msEntão, agora, e se
Entry
fosse uma aula? Esses tempos ou métricas realmente diferem tanto assim?Obviamente, a grande diferença está no redimensionamento. Alguma diferença se o Dictionary for inicializado com o Capacity? Não basta se preocupar com ... 12ms .
O que acontece é que, por
Entry
ser uma estrutura, não requer inicialização como um tipo de referência. Essa é a beleza e a desgraça do tipo de valor. Para usarEntry
como um tipo de referência, tive que inserir o seguinte código:O motivo pelo qual eu tive que inicializar cada elemento da matriz
Entry
como um tipo de referência pode ser encontrado no MSDN: Design de Estrutura . Em resumo:Na verdade, é bastante simples e vamos emprestar as Três Leis da Robótica de Asimov :
... o que tiramos disso : em resumo, seja responsável pelo uso de tipos de valor. Eles são rápidos e eficientes, mas têm a capacidade de causar muitos comportamentos inesperados, se não forem adequadamente mantidos (por exemplo, cópias não intencionais).
fonte
Decimal
ouDateTime
], se não cumprir as outras três regras, deve ser substituída por uma classe. Se uma estrutura possui uma coleção fixa de variáveis, cada uma das quais pode conter qualquer valor que seja válido para seu tipo [por exemploRectangle
], deve obedecer a regras diferentes , algumas das quais são contrárias às estruturas de "valor único" .Dictionary
tipo de entrada com base em que é apenas um tipo interno, o desempenho foi considerado mais importante que a semântica, ou alguma outra desculpa. Meu argumento é que um tipo comoRectangle
deve ter seu conteúdo exposto como campos editáveis individualmente, não "porque" os benefícios de desempenho superam as imperfeições semânticas resultantes, mas porque o tipo representa semanticamente um conjunto fixo de valores independentes e, portanto, a estrutura mutável é melhor desempenho e semanticamente superior .Sempre que você:
A ressalva, no entanto, é que as estruturas (arbitrariamente grandes) são mais caras de se passar do que as referências de classe (geralmente uma palavra de máquina), portanto as classes podem acabar sendo mais rápidas na prática.
fonte
(Guid)null
(não há problema em converter um nulo em um tipo de referência), entre outras coisas.Eu não concordo com as regras dadas no post original. Aqui estão as minhas regras:
1) Você usa estruturas de desempenho quando armazenadas em matrizes. (veja também Quando as estruturas são a resposta? )
2) Você precisa deles no código que passa dados estruturados de / para C / C ++
3) Não use estruturas a menos que precise delas:
fonte
struct
para saber como se comportará, mas se algo éstruct
com campos expostos, é tudo o que se precisa saber. Se um objeto expõe uma propriedade de um tipo de estrutura de campo exposto e se o código lê essa estrutura em uma variável e se modifica, é possível prever com segurança que essa ação não afetará o objeto cuja propriedade foi lida, a menos ou até que a estrutura seja gravada de volta. Por outro lado, se a propriedade fosse um tipo mutável classe, lê-lo e modificá-lo pode atualizar o objeto subjacente como esperado, mas ...Use uma estrutura quando desejar valor semântica, em vez de referenciar semântica.
Editar
Não sei por que as pessoas estão votando contra isso, mas este é um ponto válido, e foi feito antes da operação esclarecer sua pergunta, e é a razão básica mais fundamental para uma estrutura.
Se você precisar de semântica de referência, precisará de uma classe e não de uma estrutura.
fonte
Além da resposta "é um valor", um cenário específico para o uso de estruturas é quando você sabe que possui um conjunto de dados que está causando problemas de coleta de lixo e possui muitos objetos. Por exemplo, uma grande lista / matriz de instâncias de Pessoa. A metáfora natural aqui é uma classe, mas se você tiver um grande número de instâncias Person de vida longa, elas poderão acabar entupindo o GEN-2 e causando paradas do GC. Se o cenário o justificar, uma abordagem potencial aqui é usar uma matriz (não lista) de estruturas Person , ou seja
Person[]
. Agora, em vez de ter milhões de objetos no GEN-2, você tem um único pedaço no LOH (não estou assumindo seqüências de caracteres etc. - aqui, um valor puro sem referências). Isso tem muito pouco impacto no GC.Trabalhar com esses dados é complicado, pois os dados provavelmente estão superdimensionados para uma estrutura e você não deseja copiar valores de gordura o tempo todo. No entanto, acessá-lo diretamente em uma matriz não copia a estrutura - ela está no local (contraste com um indexador de lista, que copia). Isso significa muito trabalho com índices:
Observe que manter os próprios valores imutáveis ajudará aqui. Para uma lógica mais complexa, use um método com um parâmetro by-ref:
Novamente, isso está no lugar - não copiamos o valor.
Em cenários muito específicos, essa tática pode ser muito bem-sucedida; no entanto, é um cenário bastante avançado que deve ser tentado apenas se você souber o que está fazendo e por quê. O padrão aqui seria uma classe.
fonte
List
Eu acredito, usa umArray
bastidores. não ?Na especificação da linguagem C # :
Uma alternativa é fazer do Point uma estrutura.
Agora, apenas um objeto é instanciado - o da matriz - e as instâncias Point são armazenadas em linha na matriz.
Os construtores Struct são chamados com o novo operador, mas isso não implica que a memória esteja sendo alocada. Em vez de alocar dinamicamente um objeto e retornar uma referência a ele, um construtor struct simplesmente retorna o próprio valor struct (normalmente em um local temporário na pilha), e esse valor é copiado conforme necessário.
Com as classes, é possível que duas variáveis façam referência ao mesmo objeto e, portanto, que operações em uma variável afetem o objeto referenciado pela outra variável. Com estruturas, cada uma das variáveis possui sua própria cópia dos dados, e não é possível que operações em uma afetem a outra. Por exemplo, a saída produzida pelo seguinte fragmento de código depende se Point é uma classe ou uma estrutura.
Se Point é uma classe, a saída é 20 porque aeb fazem referência ao mesmo objeto. Se Point for uma estrutura, a saída será 10 porque a atribuição de a a b cria uma cópia do valor, e essa cópia não é afetada pela atribuição subsequente a ax
O exemplo anterior destaca duas das limitações de estruturas. Primeiro, copiar uma estrutura inteira normalmente é menos eficiente do que copiar uma referência de objeto, portanto, a atribuição e a passagem de parâmetros de valor podem ser mais caras com estruturas do que com tipos de referência. Segundo, exceto pelos parâmetros ref e out, não é possível criar referências a estruturas, o que descarta seu uso em várias situações.
fonte
ref
a uma estrutura mutável e saber que quaisquer mutações que o método externo executará nele serão feitas antes que ele retorne. É muito ruim .net não tem nenhum conceito de parâmetros efêmeros e valores de retorno de função, desde que ... #ref
fosse alcançada com objetos de classe. Essencialmente, variáveis locais, parâmetros e valores de retorno de função podem ser persistentes (padrão), retornáveis ou efêmeros. O código seria proibido de copiar coisas efêmeras para qualquer coisa que sobreviveria ao presente escopo. Coisas retornáveis seriam coisas efêmeras, exceto que elas poderiam ser retornadas de uma função. O valor de retorno de uma função seria limitado pelas restrições mais rígidas aplicáveis a qualquer um dos seus parâmetros "retornáveis".As estruturas são boas para a representação atômica dos dados, onde os dados podem ser copiados várias vezes pelo código. A clonagem de um objeto geralmente é mais cara do que copiar uma estrutura, pois envolve alocar a memória, executar o construtor e desalocar / coletar lixo quando terminar com ele.
fonte
Aqui está uma regra básica.
Se todos os campos de membro forem tipos de valor, crie uma estrutura .
Se qualquer campo de um membro for um tipo de referência, crie uma classe . Isso ocorre porque o campo do tipo de referência precisará da alocação de heap de qualquer maneira.
Exmaples
fonte
string
são semanticamente equivalentes a valores e armazenar uma referência a um objeto imutável em um campo não implica uma alocação de heap. A diferença entre uma estrutura com campos públicos expostos e um objeto de classe com campos públicos expostos é que, dada a sequência do códigovar q=p; p.X=4; q.X=5;
,p.X
terá o valor 4 sea
for um tipo de estrutura e 5 se for um tipo de classe. Se alguém deseja modificar convenientemente os membros do tipo, deve selecionar 'classe' ou 'estrutura' com base em se deseja que as alteraçõesq
afetemp
.ArraySegment<T>
encapsula aT[]
, que é sempre um tipo de classe. O tipo de estruturaKeyValuePair<TKey,TValue>
é frequentemente usado com tipos de classe como parâmetros genéricos.Primeiro: cenários de interoperabilidade ou quando você precisa especificar o layout da memória
Segundo: quando os dados tiverem quase o mesmo tamanho que um ponteiro de referência.
fonte
Você precisa usar uma "estrutura" nas situações em que deseja especificar explicitamente o layout da memória usando o StructLayoutAttribute - normalmente para PInvoke.
Edit: Comment indica que você pode usar classe ou estrutura com StructLayoutAttribute e isso certamente é verdade. Na prática, você normalmente usaria uma estrutura - ela é alocada na pilha versus a pilha, o que faz sentido se você está apenas passando um argumento para uma chamada de método não gerenciado.
fonte
Uso estruturas para empacotar ou descompactar qualquer tipo de formato de comunicação binário. Isso inclui ler ou gravar em disco, listas de vértices do DirectX, protocolos de rede ou lidar com dados criptografados / compactados.
As três diretrizes que você listou não foram úteis para mim neste contexto. Quando eu precisar escrever quatrocentos bytes de material em uma Ordem Particular, vou definir uma estrutura de quatrocentos bytes e preenchê-la com quaisquer valores não relacionados que ela deva ter, e vou configurá-lo de qualquer maneira que faça mais sentido no momento. (Certo, quatrocentos bytes seriam bem estranhos - mas quando eu estava escrevendo arquivos do Excel para viver, estava lidando com estruturas de até quarenta bytes por toda parte, porque é o tamanho de alguns dos registros BIFF.)
fonte
Com exceção dos tipos de valor que são usados diretamente pelo tempo de execução e vários outros para fins de PInvoke, você só deve usar tipos de valor em 2 cenários.
fonte
this
parâmetro efêmero usado para invocar seus métodos); classes permitem duplicar referências.O .NET suporta
value types
ereference types
(em Java, você pode definir apenas tipos de referência). Instâncias dereference types
serem alocadas no heap gerenciado e são coletadas como lixo quando não há referências pendentes para elas. Instâncias devalue types
, por outro lado, são alocadas nastack
memória e, portanto, alocada são recuperadas assim que seu escopo termina. E, é claro,value types
passe pelo valor ereference types
pela referência. Todos os tipos de dados primitivos em C #, exceto System.String, são tipos de valor.Quando usar struct sobre a classe,
Em C #,
structs
arevalue types
, classes arereference types
. Você pode criar tipos de valor, em C #, usando aenum
palavra - chave e astruct
palavra - chave. Usar um emvalue type
vez de areference type
resultará em menos objetos no heap gerenciado, o que resulta em menor carga no coletor de lixo (GC), ciclos de GC menos frequentes e, consequentemente, melhor desempenho. No entanto, tambémvalue types
têm suas desvantagens. Passar por um grandestruct
é definitivamente mais caro do que passar uma referência, esse é um problema óbvio. O outro problema é a sobrecarga associadaboxing/unboxing
. Caso esteja se perguntando o queboxing/unboxing
significa, siga estes links para obter uma boa explicaçãoboxing
eunboxing
. Além do desempenho, há momentos em que você simplesmente precisa que os tipos possuam semântica de valores, o que seria muito difícil (ou feio) de implementar, se houverreference types
tudo o que você tem. Você deve usarvalue types
apenas Quando você precisar copiar semântica ou precisar de inicialização automática, normalmentearrays
nesses tipos.fonte
ref
. Passar qualquer estrutura de tamanho porref
custos é o mesmo que passar uma referência de classe por valor. Copiar qualquer estrutura de tamanho ou passar por valor é mais barato do que executar uma cópia defensiva de um objeto de classe e armazenar ou passar uma referência a isso. As grandes épocas das classes são melhores do que as estruturas para armazenar valores são (1) quando as classes são imutáveis (para evitar cópias defensivas), e cada instância criada é transmitida muito, ou ...readOnlyStruct.someMember = 5;
não é criarsomeMember
uma propriedade somente leitura, mas transformá-la em um campo.Uma estrutura é um tipo de valor. Se você atribuir uma estrutura a uma nova variável, a nova variável conterá uma cópia do original.
A execução dos seguintes resultados em 5 instâncias da estrutura armazenada na memória:
Uma classe é um tipo de referência. Quando você atribui uma classe a uma nova variável, a variável contém uma referência ao objeto de classe original.
A execução dos seguintes resultados em apenas uma instância do objeto de classe na memória.
Struct s pode aumentar a probabilidade de um erro de código. Se um objeto de valor for tratado como um objeto de referência mutável, um desenvolvedor poderá se surpreender quando as alterações feitas forem inesperadamente perdidas.
fonte
Fiz uma pequena referência com o BenchmarkDotNet para entender melhor o benefício "struct" em números. Estou testando loop através de matriz (ou lista) de estruturas (ou classes). Criar essas matrizes ou listas está fora do escopo do benchmark - é claro que "classe" é mais pesada utilizará mais memória e envolverá o GC.
Portanto, a conclusão é: tenha cuidado com o LINQ e as estruturas ocultas boxe / unboxing e o uso de estruturas para micro-otimizações permanecem estritamente com matrizes.
PS: Outra referência sobre a passagem de struct / class pela pilha de chamadas existe https://stackoverflow.com/a/47864451/506147
Código:
fonte
Tipos de estrutura em C # ou outras linguagens .net geralmente são usados para armazenar coisas que devem se comportar como grupos de valores de tamanho fixo. Um aspecto útil dos tipos de estrutura é que os campos de uma instância do tipo de estrutura podem ser modificados modificando o local de armazenamento em que ela é mantida e de nenhuma outra maneira. É possível codificar uma estrutura de tal maneira que a única maneira de alterar qualquer campo é construir uma instância nova e usar uma atribuição de estrutura para alterar todos os campos do destino, substituindo-os por valores da nova instância, mas a menos que uma estrutura não forneça meios de criar uma instância em que seus campos tenham valores não padrão, todos os seus campos serão mutáveis se e se a própria estrutura estiver armazenada em um local mutável.
Observe que é possível projetar um tipo de estrutura para que ele se comporte essencialmente como um tipo de classe, se a estrutura contiver um campo particular do tipo de classe e redirecione seus próprios membros para o objeto de classe agrupado. Por exemplo, a
PersonCollection
pode oferecer propriedadesSortedByName
eSortedById
, ambas com uma referência "imutável" aPersonCollection
(definida no construtor) e implementadaGetEnumerator
chamandocreator.GetNameSortedEnumerator
oucreator.GetIdSortedEnumerator
. Tais estruturas se comportariam como uma referência aPersonCollection
, exceto que seusGetEnumerator
métodos seriam vinculados a métodos diferentes noPersonCollection
. Também é possível que uma estrutura envolva uma parte de uma matriz (por exemplo, pode-se definir umaArrayRange<T>
estrutura que conteria umT[]
chamadoArr
, um intOffset
e um intLength
, com uma propriedade indexada que, para um índiceidx
no intervalo de 0 aLength-1
, acessariaArr[idx+Offset]
). Infelizmente, sefoo
for uma instância somente leitura dessa estrutura, as versões atuais do compilador não permitirão operações comofoo[3]+=4;
porque elas não têm como determinar se essas operações tentariam gravar nos campos defoo
.Também é possível projetar uma estrutura para se comportar como um tipo de valor que contém uma coleção de tamanho variável (que parecerá ser copiada sempre que a estrutura for), mas a única maneira de fazer esse trabalho é garantir que nenhum objeto para o qual struct mantém uma referência que será exposta a qualquer coisa que possa sofrer uma mutação. Por exemplo, pode-se ter uma estrutura do tipo matriz que contém uma matriz privada e cujo método "put" indexado cria uma nova matriz cujo conteúdo é semelhante ao original, exceto por um elemento alterado. Infelizmente, pode ser um pouco difícil fazer com que essas estruturas funcionem com eficiência. Embora haja momentos em que a semântica da estrutura pode ser conveniente (por exemplo, ser capaz de passar uma coleção do tipo matriz para uma rotina, com o chamador e o destinatário sabendo que o código externo não modifica a coleção,
fonte
Nah - eu não concordo totalmente com as regras. São boas diretrizes a serem consideradas com desempenho e padronização, mas não à luz das possibilidades.
Como você pode ver nas respostas, existem várias maneiras criativas de usá-las. Portanto, essas diretrizes precisam ser apenas isso, sempre por uma questão de desempenho e eficiência.
Nesse caso, eu uso classes para representar objetos do mundo real em sua forma maior, uso estruturas para representar objetos menores que têm usos mais exatos. Do jeito que você disse, "um todo mais coeso". A palavra-chave é coesa. As classes serão mais elementos orientados a objetos, enquanto estruturas podem ter algumas dessas características, embora em menor escala. IMO.
Eu os uso muito nas tags Treeview e Listview, onde atributos estáticos comuns podem ser acessados muito rapidamente. Eu sempre lutei para obter essa informação de outra maneira. Por exemplo, em meus aplicativos de banco de dados, eu uso um Treeview em que tenho Tabelas, SPs, Funções ou quaisquer outros objetos. Eu crio e preencho minha estrutura, coloco na tag, retiro, obtenho os dados da seleção e assim por diante. Eu não faria isso com uma aula!
Eu tento mantê-los pequenos, usá-los em situações de instância única e impedir que eles mudem. É prudente estar ciente da memória, alocação e desempenho. E o teste é tão necessário.
fonte
double
valores para essas coordenadas, essa especificação o obrigaria a comportar-se semanticamente de forma idêntica a uma estrutura de campo exposto, exceto por alguns detalhes de comportamento multithread (a classe imutável seria melhor em alguns casos, enquanto a estrutura de campo exposto seria melhor em outras; uma estrutura chamada "imutável" seria pior em todos os casos).Minha regra é
1, sempre use classe;
2, se houver algum problema de desempenho, tento alterar alguma classe para estruturar, dependendo das regras mencionadas pela @IAbstract e, em seguida, faço um teste para verificar se essas alterações podem melhorar o desempenho.
fonte
Foo
encapsule uma coleção fixa de valores independentes (por exemplo, coordenadas de um ponto) que, às vezes, você deseja passar como grupo e às vezes deseja mudar de forma independente. Eu não encontrei nenhum padrão para usar classes que combine ambos os propósitos tão bem quanto uma estrutura simples de campo exposto (que, sendo uma coleção fixa de variáveis independentes, se encaixa perfeitamente).public readonly
campos nos meus tipos, porque a criação de propriedades somente leitura é simplesmente muito trabalhosa para praticamente nenhum benefício.)MyListOfPoint[3].Offset(2,3);
emvar temp=MyListOfPoint[3]; temp.Offset(2,3);
uma transformação que é falsa quando aplicada ...Offset
método. A maneira correta de impedir esse código falso não deve tornar as estruturas desnecessariamente imutáveis, mas permitir que métodos comoOffset
sejam marcados com um atributo que proíba a transformação mencionada acima. Conversões numéricas implícitas também poderiam ter sido muito melhores se pudessem ser etiquetadas de forma a serem aplicáveis apenas nos casos em que sua chamada seria óbvia. Se existem sobrecargas parafoo(float,float)
efoo(double,double)
, gostaria de postular que a tentativa de usar umfloat
edouble
muitas vezes não se deve aplicar uma conversão implícita, mas deve antes ser um erro.double
valor afloat
, ou transmiti-lo a um método que pode levar a umfloat
argumento, mas não odouble
faria, quase sempre faria o que o programador pretendia. Por outro lado, atribuirfloat
expressão adouble
sem uma conversão de texto explícita geralmente é um erro. O único momento em que permitir adouble->float
conversão implícita causaria problemas seria quando uma sobrecarga abaixo do ideal seria selecionada. Eu diria que o caminho certo para evitar isso não deveria ser proibir implícito double-> float, mas marcar sobrecargas com atributos para impedir a conversão.Uma classe é um tipo de referência. Quando um objeto da classe é criado, a variável à qual o objeto está atribuído mantém apenas uma referência a essa memória. Quando a referência do objeto é atribuída a uma nova variável, a nova variável se refere ao objeto original. As alterações feitas através de uma variável são refletidas na outra variável porque ambas se referem aos mesmos dados. Uma estrutura é um tipo de valor. Quando uma estrutura é criada, a variável à qual a estrutura é atribuída mantém os dados reais da estrutura. Quando a estrutura é atribuída a uma nova variável, ela é copiada. A nova variável e a variável original contêm, portanto, duas cópias separadas dos mesmos dados. As alterações feitas em uma cópia não afetam a outra cópia. Em geral, as classes são usadas para modelar comportamentos mais complexos ou dados que devem ser modificados após a criação de um objeto de classe.
Classes e estruturas (Guia de Programação em C #)
fonte
MITO 1: ESTRUTURAS SÃO CLASSES LEVE
Este mito vem em uma variedade de formas. Algumas pessoas acreditam que os tipos de valor não podem ou não devem ter métodos ou outro comportamento significativo - eles devem ser usados como tipos simples de transferência de dados, com apenas campos públicos ou propriedades simples. O tipo DateTime é um bom contra-exemplo: faz sentido que seja um tipo de valor, em termos de ser uma unidade fundamental como um número ou um caractere, e também faz sentido poder executar cálculos com base em seu valor. Observando as coisas de outra direção, os tipos de transferência de dados geralmente devem ser de referência - a decisão deve se basear no valor desejado ou na semântica do tipo de referência, não na simplicidade do tipo. Outras pessoas acreditam que os tipos de valor são "mais leves" do que os tipos de referência em termos de desempenho. A verdade é que, em alguns casos, os tipos de valor são mais eficientes - eles não exigem coleta de lixo, a menos que estejam em caixas, não possuem a sobrecarga de identificação de tipo e não exigem desreferenciamento, por exemplo. Mas, de outras formas, os tipos de referência são mais eficientes - passagem de parâmetros, atribuição de valores a variáveis, retorno de valores e operações semelhantes exigem que apenas 4 ou 8 bytes sejam novamente copiados (dependendo de você estar executando o CLR de 32 ou 64 bits ) em vez de copiar todos os dados. Imagine se ArrayList fosse de alguma forma um tipo de valor "puro" e passar uma expressão ArrayList para um método envolvido na cópia de todos os seus dados! Em quase todos os casos, o desempenho não é realmente determinado por esse tipo de decisão. Os gargalos quase nunca estão onde você pensa que estarão; antes de tomar uma decisão de design com base no desempenho, você deve medir as diferentes opções. Vale a pena notar que a combinação das duas crenças também não funciona. Não importa quantos métodos um tipo possui (seja uma classe ou uma estrutura) - a memória usada por instância não é afetada. (Existe um custo em termos de memória ocupada pelo próprio código, mas é incorrida uma vez e não para cada instância.)
MITO 2: TIPOS DE REFERÊNCIA VIVEM NO PÃO; TIPOS DE VALOR AO VIVO NA PILHA
Este geralmente é causado pela preguiça por parte da pessoa que o repete. A primeira parte está correta - uma instância de um tipo de referência é sempre criada no heap. É a segunda parte que causa problemas. Como já observei, o valor de uma variável vive onde quer que seja declarado; portanto, se você tiver uma classe com uma variável de instância do tipo int, o valor dessa variável para qualquer objeto sempre estará sempre onde estão os demais dados do objeto. na pilha. Somente variáveis locais (variáveis declaradas nos métodos) e parâmetros do método vivem na pilha. No C # 2 e versões posteriores, mesmo algumas variáveis locais não ficam realmente na pilha, como você verá quando analisamos métodos anônimos no capítulo 5. ESTES CONCEITOS SÃO RELEVANTES AGORA? É discutível que, se você estiver escrevendo um código gerenciado, deixe o tempo de execução se preocupar com a melhor maneira de usar a memória. De fato, a especificação da linguagem não garante o que mora onde; um tempo de execução futuro poderá criar alguns objetos na pilha, se souber que pode se safar, ou o compilador C # poderá gerar código que dificilmente usa a pilha. O próximo mito é geralmente apenas uma questão terminológica.
MITO 3: OS OBJETOS SÃO PASSADOS POR REFERÊNCIA EM C # POR PADRÃO
Este é provavelmente o mito mais amplamente propagado. Mais uma vez, as pessoas que fazem essa afirmação com frequência (embora nem sempre) sabem como o C # realmente se comporta, mas não sabem o que "passar por referência" realmente significa. Infelizmente, isso é confuso para as pessoas que sabem o que isso significa. A definição formal de passagem por referência é relativamente complicada, envolvendo valores de l e terminologia semelhante da ciência da computação, mas o importante é que, se você passar uma variável por referência, o método que você está chamando pode alterar o valor da variável do chamador alterando seu valor de parâmetro. Agora, lembre-se de que o valor de uma variável do tipo de referência é a referência, não o próprio objeto. Você pode alterar o conteúdo do objeto ao qual um parâmetro se refere sem que o próprio parâmetro seja passado por referência. Por exemplo,
Quando esse método é chamado, o valor do parâmetro (uma referência a um StringBuilder) é passado por valor. Se você alterasse o valor da variável do construtor dentro do método - por exemplo, com a instrução construtor = null; - essa alteração não seria vista pelo chamador, ao contrário do mito. É interessante notar que não apenas o bit "por referência" do mito é impreciso, mas também o bit "objetos são passados". Os próprios objetos nunca são passados, nem por referência nem por valor. Quando um tipo de referência está envolvido, a variável é passada por referência ou o valor do argumento (a referência) é passado por valor. Além de qualquer outra coisa, isso responde à pergunta do que acontece quando nulo é usado como argumento por valor - se objetos estivessem sendo distribuídos, isso causaria problemas, pois não haveria um objeto para passar! Em vez de, a referência nula é passada por valor da mesma maneira que qualquer outra referência seria. Se essa rápida explicação o deixou perplexo, convém examinar meu artigo "Parâmetro passando em C #" (http://mng.bz/otVt ), que entra em muito mais detalhes. Esses mitos não são os únicos por aí. O boxe e o unboxing são uma boa parte de mal-entendidos, que tentarei esclarecer a seguir.
Referência: C # in Depth 3rd Edition por Jon Skeet
fonte
Eu acho que uma boa primeira aproximação é "nunca".
Eu acho que uma boa segunda aproximação é "nunca".
Se você está desesperado por perf, considere-os, mas sempre meça.
fonte
Eu estava lidando com o Pipe nomeado do Windows Communication Foundation [WCF] e notei que faz sentido usar Structs para garantir que a troca de dados seja do tipo valor em vez do tipo de referência .
fonte
A estrutura C # é uma alternativa leve para uma classe. Pode fazer quase o mesmo que uma classe, mas é menos "caro" usar uma estrutura do que uma classe. A razão para isso é um pouco técnica, mas, para resumir, novas instâncias de uma classe são colocadas no heap, onde estruturas recém-instanciadas são colocadas na pilha. Além disso, você não está lidando com referências a estruturas, como em classes, mas está trabalhando diretamente com a instância struct. Isso também significa que quando você passa uma estrutura para uma função, é por valor, e não como referência. Há mais sobre isso no capítulo sobre parâmetros de função.
Portanto, você deve usar estruturas quando desejar representar estruturas de dados mais simples e, principalmente, se souber que irá instanciar muitas delas. Existem muitos exemplos na estrutura .NET, onde a Microsoft usou estruturas em vez de classes, por exemplo, a estrutura Point, Rectangle e Color.
fonte
O Struct pode ser usado para melhorar o desempenho da coleta de lixo. Embora você normalmente não precise se preocupar com o desempenho do GC, há situações em que ele pode ser um assassino. Como caches grandes em aplicativos de baixa latência. Veja esta postagem para um exemplo:
http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/
fonte
Os tipos de estrutura ou valor podem ser usados nos seguintes cenários -
Você pode saber mais sobre os tipos e valores aqui neste link
fonte
Resumidamente, use struct se:
1- as propriedades / campos do seu objeto não precisam ser alterados. Quero dizer, você só quer dar a eles um valor inicial e depois lê-los.
2 - as propriedades e os campos do seu objeto são do tipo valor e não são tão grandes.
Nesse caso, você pode aproveitar as estruturas para obter um melhor desempenho e alocação de memória otimizada, pois elas usam apenas pilhas, em vez de pilhas e pilhas (nas classes)
fonte
Eu raramente uso uma estrutura para as coisas. Mas sou só eu. Depende se eu preciso que o objeto seja anulável ou não.
Conforme declarado em outras respostas, eu uso classes para objetos do mundo real. Eu também tenho a mentalidade de estruturas são usadas para armazenar pequenas quantidades de dados.
fonte
Estruturas são, em muitos aspectos, como classes / objetos. A estrutura pode conter funções, membros e pode ser herdada. Mas as estruturas são em C # usadas apenas para retenção de dados . As estruturas precisam de menos RAM do que as classes e são mais fáceis para o coletor de lixo coletar . Mas quando você usa funções em sua estrutura, o compilador realmente leva essa estrutura de maneira muito semelhante à classe / objeto; portanto, se você deseja algo com funções, use classe / objeto .
fonte