Embora as structs em C# sejam tipos de valor e normalmente sejam armazenadas na stack (pilha), elas podem ser alocadas no heap (monte) em algumas situações.
Quando uma struct pode ser armazenada no heap?
Quando faz parte de uma classe
Se uma struct for um campo dentro de uma class, ela será armazenada junto com a instância da classe no heap.
public struct MinhaStruct
{
public int Valor;
}
public class MinhaClasse
{
public MinhaStruct Data; // Armazenado no heap junto com a instância da classe
}
Quando é "boxed" (boxing)
Se uma struct for convertida para object ou para uma interface que ela implementa, ela será "encaixotada" (boxed), ou seja, será copiada para o heap e tratada como uma referência.
struct MinhaStruct
{
public int Valor;
}
public class UsandoMinhaStruct
{
public void UsarMinhaStruct()
{
object obj = new MinhaStruct { Valor = 10 }; // Boxing: agora está no heap
}
}
Quando usada em um "closure" (captura de variável local em um delegate ou lambda)
Se uma struct for capturada por um closure dentro de um delegate ou expressão lambda, o compilador pode promovê-la para o heap.
struct MinhaStruct
{
public int Valor;
}
public class MinhaClasse
{
Func<int> CriarLambda()
{
MinhaStruct s = new MinhaStruct { Valor = 42 };
return () => s.Valor; // Pode levar à alocação no heap
}
}
Mas, se você deseja forçar que uma struct nunca seja colocada no heap, você pode usar a palavra ref antes de struct.
Quando usar ref struct?
O principal caso de uso para ref struct é quando você precisa garantir que uma struct nunca seja movida para o heap, como em Span<T>. Isso é útil para evitar alocações desnecessárias e melhorar a performance.
Exemplo prático:
public ref struct MinhaRefStruct
{
public int Valor;
public MinhaRefStruct(int valor)
{
Valor = valor;
}
}
public class MinhaClasse
{
// ERRO: 'ref struct' não pode ser um campo de uma classe
// public MinhaRefStruct Data;
}
Se tentarmos armazenar essa ref struct dentro de uma classe normal, o compilador não permitirá. Isso acontece porque as class são armazenadas no heap, e ref struct não pode ser alocada no heap.
Assim, a palavra ref antes de struct torna a estrutura restrita à pilha, impedindo que ela seja usada de forma incorreta, como dentro de classes ou em coleções que podem realocá-la na memória. Isso pode melhorar a performance em alguns cenários, mas adiciona várias restrições.
Principais diferenças entre struct e ref struct
Característica | struct (normal) | ref struct |
Local de alocação | Heap ou Stack | Apenas Stack |
Pode ser usada em async? | Sim | Não |
Pode ser usada como campo de uma class? | Sim | Não |
Pode ser usada em coleções do tipo List<T> ou Array<T>? | Sim | Não |
Conclusão
Embora struct seja um tipo de valor e normalmente fique na stack, em alguns casos pode acabar no heap (como quando faz parte de uma classe ou sofre boxing). Já ref struct sempre permanecerá na stack.