Pergunta

É possível em C# ter um Struct com uma variável de membro que seja do tipo Class?Em caso afirmativo, onde as informações são armazenadas, na pilha, na pilha ou em ambos?

Foi útil?

Solução

Sim você pode.O ponteiro para a variável do membro da classe é armazenado na pilha com o restante dos valores da estrutura e os dados da instância da classe são armazenados no heap.

As estruturas também podem conter definições de classe como membros (classes internas).

Aqui está um código realmente inútil que pelo menos compila e executa para mostrar que é possível:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyStr m = new MyStr();
            m.Foo();

            MyStr.MyStrInner mi = new MyStr.MyStrInner();
            mi.Bar();

            Console.ReadLine();
        }
    }

    public class Myclass
    {
        public int a;
    }

    struct MyStr
    {
        Myclass mc;

        public void Foo()
        {
            mc = new Myclass();
            mc.a = 1;
        }

        public class MyStrInner
        {
            string x = "abc";

            public string Bar()
            {
                return x;
            }
        }
    }
}

Outras dicas

O conteúdo da classe é armazenado no heap.

Uma referência à classe (que é quase igual a um ponteiro) é armazenada com o conteúdo da estrutura.O local onde o conteúdo da estrutura é armazenado depende se é uma variável local, parâmetro de método ou membro de uma classe, e se foi encaixotado ou capturado por um fechamento.

Se um dos campos de uma estrutura for um tipo de classe, esse campo conterá o identidade de um objeto de classe ou então uma referência nula.Se o objeto de classe em questão for imutável (por exemplo string), armazenar sua identidade também armazenará efetivamente seu conteúdo.Entretanto, se o objeto de classe em questão for mutável, armazenar a identidade será um meio eficaz de armazenar o conteúdo. se e somente se a referência nunca cair nas mãos de qualquer código que possa alterá-la uma vez armazenada no campo.

Geralmente, deve-se evitar armazenar tipos de classes mutáveis ​​dentro de uma estrutura, a menos que uma das duas situações se aplique:

  1. O que nos interessa é, de facto, a identidade do objecto de classe e não o seu conteúdo.Por exemplo, pode-se definir uma estrutura `FormerControlBounds` que contém campos do tipo `Control` e `Rectangle`, e representa os `Bounds` que o controle teve em algum momento, com o propósito de poder restaurar posteriormente o controle para sua posição anterior.O objetivo do campo `Control` não seria manter uma cópia do estado do controle, mas sim identificar o controle cuja posição deveria ser restaurada.Geralmente, a estrutura deve evitar acessar quaisquer membros mutáveis ​​do objeto ao qual ela contém uma referência, exceto nos casos em que é claro que tal acesso se refere ao estado mutável atual do objeto em questão (por exemplo,em um método `CaptureControlPosition` ou `RestoreControlToCapturedPosition`, ou uma propriedade `ControlHasMoved`).
  2. O campo é `privado`, os únicos métodos que o lêem o fazem com o propósito de examinar suas propriedades sem expor o próprio objeto a código externo, e os únicos métodos que o escrevem criarão um novo objeto, realizarão todas as mutações que vão acontecer com ele e, em seguida, armazene uma referência para esse objeto.Poderíamos, por exemplo, projetar uma `struct` que se comportasse como um array, mas com semântica de valor, fazendo com que a struct mantivesse um array em um campo privado, e fazendo com que cada tentativa de escrever o array criasse um novo array com dados do antigo, modifique o novo array e armazene o array modificado nesse campo.Observe que mesmo que o array em si fosse um tipo mutável, cada instância do array que fosse armazenada no campo seria efetivamente imutável, uma vez que nunca seria acessível por qualquer código que pudesse alterá-lo.

Observe que o cenário nº 1 é bastante comum com tipos genéricos;por exemplo, é muito comum ter um dicionário cujos “valores” são as identidades de objetos mutáveis;enumerar esse dicionário retornará instâncias de KeyValuePair cujo Value campo contém esse tipo mutável.

O cenário nº 2 é menos comum.Infelizmente, não há como dizer ao compilador que métodos struct diferentes dos configuradores de propriedades modificarão uma struct e seu uso deve, portanto, ser proibido em contextos somente leitura;alguém poderia ter uma estrutura que se comportasse como um List<T>, mas com semântica de valor, e incluiu um Add método, mas uma tentativa de chamar Add em uma instância de struct somente leitura geraria código falso em vez de um erro do compilador.Além disso, métodos mutantes e configuradores de propriedades em tais estruturas geralmente terão um desempenho bastante ruim.Essas estruturas podem ser úteis quando existem como um wrapper imutável em uma classe que de outra forma seria mutável;se tal estrutura nunca for encaixotada, o desempenho geralmente será melhor que uma classe.Se for embalado exatamente uma vez (por ex.ao ser convertido em um tipo de interface), o desempenho geralmente será comparável ao de uma classe.Se encaixotado repetidamente, o desempenho pode ser muito pior do que uma aula.

Provavelmente não é uma prática recomendada fazer isso:ver http://msdn.microsoft.com/en-us/library/ms229017(VS.85).aspx

Os tipos de referência são alocados na pilha e o gerenciamento de memória é tratado pelo coletor de lixo.

Os tipos de valor são alocados na pilha ou embutidos e são desalocados quando saem do escopo.

Em geral, os tipos de valor são mais baratos para alocar e desalocar.No entanto, se eles forem usados ​​em cenários que exigem uma quantidade significativa de boxe e unboxing, eles têm um desempenho ruim em comparação com os tipos de referência.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top