Pergunta

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

Foi útil?

Solução

No .NET, existem duas categorias de tipos, tipos de referência e tipos de valor.

Estruturas são tipos de valor e as aulas são tipos de referência.

A diferença geral é que um tipo de referência vive na pilha, e um tipo de valor vive inline, ou seja, onde quer que sua variável ou campo esteja definido.

Uma variável contendo um tipo de valor contém todo o tipo de valor valor.Para uma estrutura, isso significa que a variável contém toda a estrutura, com todos os seus campos.

Uma variável contendo um tipo de referência contém um ponteiro ou um referência para algum outro lugar na memória onde reside o valor real.

Para começar, isso tem um benefício:

  • tipos de valor sempre contém um valor
  • tipos de referência pode conter um nulo-referência, o que significa que não se referem a nada no momento

Internamente, tipo de referências são implementados como ponteiros, e sabendo disso, e sabendo como funciona a atribuição de variáveis, existem outros padrões de comportamento:

  • copiar o conteúdo de um tipo de valor variável para outra variável, copia todo o conteúdo para a nova variável, tornando as duas distintas.Em outras palavras, após a cópia, as alterações em um não afetarão o outro
  • copiar o conteúdo de um tipo de referência variável para outra variável, copia a referência, o que significa que agora você tem duas referências para a mesma Em outro lugar armazenamento dos dados reais.Em outras palavras, após a cópia, a alteração dos dados em uma referência parecerá afetar a outra também, mas apenas porque você está apenas olhando os mesmos dados em ambos os lugares

Ao declarar variáveis ​​ou campos, veja como os dois tipos diferem:

  • variável: tipo de valor vive na pilha, tipo de referência reside na pilha como um ponteiro para algum lugar na memória heap onde reside a memória real (embora observe Série de artigos de Eric Lipperts:A pilha é um detalhe de implementação.)
  • campo de classe/estrutura: tipo de valor vive completamente dentro do tipo, tipo de referência reside dentro do tipo como um ponteiro para algum lugar na memória heap onde reside a memória real.

Outras dicas

Um breve resumo de cada um:

Apenas aulas:

  • Pode suportar herança
  • São tipos de referência (ponteiro)
  • A referência pode ser nula
  • Tem sobrecarga de memória por nova instância

Somente estruturas:

  • Não é possível suportar herança
  • São tipos de valor
  • São passados ​​​​por valor (como números inteiros)
  • Não pode ter uma referência nula (a menos que Nullable seja usado)
  • Não tenha sobrecarga de memória por nova instância - a menos que esteja 'em caixa'

Classes e estruturas:

  • Os tipos de dados compostos são normalmente usados ​​para conter algumas variáveis ​​que possuem algum relacionamento lógico?
  • Pode conter métodos e eventos
  • Pode suportar interfaces

No .NET, as declarações de estrutura e classe diferenciam entre tipos de referência e tipos de valor.

Quando você passa um tipo de referência, apenas um é realmente armazenado.Todo o código que acessa a instância está acessando a mesma.

Quando você passa um tipo de valor, cada um é uma cópia.Todo o código está funcionando em sua própria cópia.

Isso pode ser mostrado com um exemplo:

struct MyStruct 
{
    string MyProperty { get; set; }
}

void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}

...

// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 

ChangeMyStruct(testStruct);

// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

Para uma aula isso seria diferente

class MyClass 
{
    string MyProperty { get; set; }
}

void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}

...

// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };

ChangeMyClass(testClass);

// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

As classes não podem ser nada – a referência pode apontar para um nulo.

As estruturas são o valor real - elas podem estar vazias, mas nunca nulas.Por esta razão, as estruturas sempre têm um construtor padrão sem parâmetros - elas precisam de um 'valor inicial'.

Da Microsoft Escolhendo entre classe e estrutura ...

Como regra geral, a maioria dos tipos em uma estrutura deve ser Classes.Há, no entanto, algumas situações em que o características de um tipo de valor torná-lo mais apropriado para usar estruturas.

CONSIDERE uma estrutura em vez de uma classe:

  • Se as instâncias do tipo forem pequenas e geralmente de curta duração ou geralmente incorporadas em outros objetos.

X EVITE uma estrutura a menos que o tipo tenha todos dos seguintes Características:

  • Representa logicamente um único valor, semelhante aos tipos primitivos (int, double, etc.).
  • Tem um tamanho de instância inferior a 16 bytes.
  • É imutável. (não pode ser mudado)
  • Não precisará ser encaixotado com frequência.

Além de todas as diferenças descritas nas outras respostas:

  1. Estruturas não pode ter um construtor explícito sem parâmetros enquanto uma classe pode
  2. Estruturas não pode ter destruidores, enquanto uma classe pode
  3. Estruturas não posso herdar de outra estrutura ou classe, enquanto uma classe pode herdar de outra classe.(Tanto estruturas quanto classes podem ser implementadas a partir de uma interface.)

Se você está atrás de um vídeo explicando todas as diferenças, você pode conferir Parte 29 - Tutorial C# - Diferença entre classes e structs em C#.

Instâncias de classes são armazenadas no heap gerenciado.Todas as variáveis ​​que 'contêm' uma instância são simplesmente uma referência à instância no heap.Passar um objeto para um método resulta na passagem de uma cópia da referência, não do objeto em si.

Estruturas (tecnicamente, tipos de valor) são armazenadas onde quer que sejam usadas, como um tipo primitivo.O conteúdo pode ser copiado pelo tempo de execução a qualquer momento e sem invocar um construtor de cópia customizado.Passar um tipo de valor para um método envolve copiar o valor inteiro, novamente sem invocar nenhum código personalizável.

A distinção é melhor feita pelos nomes C++/CLI:"classe ref" é uma classe conforme descrita primeiro, "classe de valor" é uma classe conforme descrita em segundo lugar.As palavras-chave "class" e "struct" usadas pelo C# são simplesmente algo que deve ser aprendido.

Diferença entre estruturas e classes:

  • Estruturas são do tipo valor enquanto As aulas são do tipo de referência.
  • Estruturas são armazenadas na pilha enquanto As classes são armazenadas no heap.
  • Os tipos de valor mantêm seu valor na memória onde são declarados, mas O tipo de referência contém uma referência a uma memória de objeto.
  • Tipos de valor destruídos imediatamente depois que o escopo é perdido considerando tipo de referência somente a variável destroy depois que o escopo é perdido.O objeto é posteriormente destruído pelo coletor de lixo.
  • Quando você copia struct em outra struct, uma nova cópia dessa struct é criado modificado de uma struct não afetará o valor do outras estruturas.
  • Quando você copia uma classe em outra classe, ela copia apenas o variável de referência.
  • Ambas as variáveis ​​de referência apontam para o mesmo objeto no heap.A mudança para uma variável afetará a outra variável de referência.
  • Estruturas não podem ter destruidores, mas as classes podem ter destruidores.
  • Estruturas não podem ter construtores explícitos sem parâmetros que um class can structs não oferece suporte a herança, mas classes sim.Ambos Suporte a herança de uma interface.
  • As estruturas são do tipo selada.

Estrutura vs Classe

Uma estrutura é um tipo de valor, portanto é armazenada na pilha, mas uma classe é um tipo de referência e é armazenada no heap.

Uma estrutura não suporta herança e polimorfismo, mas uma classe suporta ambos.

Por padrão, todos os membros da estrutura são públicos, mas os membros da classe são, por padrão, de natureza privada.

Como uma estrutura é um tipo de valor, não podemos atribuir nulo a um objeto struct, mas não é o caso de uma classe.

Só para completar, há outra diferença na hora de usar o Equals método, que é herdado por todas as classes e estruturas.

Digamos que temos uma classe e uma estrutura:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

e no método Main, temos 4 objetos.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

Então:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

Então, as estruturas são adequadas para objetos do tipo numérico, como pontos (exceto as coordenadas x e y).E as aulas são adequadas para outros.Mesmo que 2 pessoas tenham o mesmo nome, altura, peso..., ainda são 2 pessoas.

Para complementar as outras respostas, há uma diferença fundamental que vale a pena notar: é como ela é armazenada na memória.Isso pode ter um efeito importante no desempenho dos arrays.Estruturas são tipos de valor, portanto armazenam um valor na área da memória para a qual estão apontando, as classes são tipos de referência, portanto, referenciam uma classe na área da memória para a qual estão apontando, o valor real é armazenado em outro lugar.

  • Com uma estrutura, a memória é alocada dentro da classe que contém para armazenar os dados.
  • Com uma classe, a classe que a contém conterá apenas um ponteiro para a nova classe em uma área diferente da memória.

Isso também é verdade com arrays, então um array de estruturas fica assim na memória

[struct][struct][struct][struct][struct][struct][struct][struct]

Onde uma série de classes se parece com isto

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

Os valores reais nos quais você está interessado não são armazenados na matriz, mas em outro lugar na memória.

Para a grande maioria dos aplicativos, essa diferença realmente não importa; no entanto, em códigos de alto desempenho, isso afetará a localização dos dados na memória e terá um grande impacto no desempenho do cache da CPU.Usar classes quando você poderia/deveria ter usado estruturas aumentará enormemente o número de falhas de cache na CPU.

A coisa mais lenta que uma CPU moderna faz não é processar números, ela busca dados da memória, e uma ocorrência no cache L1 é muitas vezes mais rápida do que ler dados da RAM.

Bem, para começar, uma struct é passada por valor e não por referência.As estruturas são boas para estruturas de dados relativamente simples, enquanto as classes têm muito mais flexibilidade do ponto de vista arquitetônico por meio de polimorfismo e herança.

Outros provavelmente podem fornecer mais detalhes do que eu, mas eu uso structs quando a estrutura que procuro é simples.

Além da diferença básica do especificador de acesso, e poucos mencionados acima, gostaria de adicionar algumas das principais diferenças, incluindo algumas das mencionadas acima, com um exemplo de código com saída, que dará uma ideia mais clara da referência e do valor

Estruturas:

  • São tipos de valor e não requerem alocação de heap.
  • A alocação de memória é diferente e é armazenada na pilha
  • Útil para pequenas estruturas de dados
  • Afeta o desempenho, quando passamos valor para o método, passamos toda a estrutura de dados e tudo é passado para a pilha.
  • O construtor simplesmente retorna o próprio valor da estrutura (normalmente em um local temporário na pilha) e esse valor é então copiado conforme necessário
  • Cada uma das variáveis ​​tem sua própria cópia dos dados e não é possível que as operações em uma afetem a outra.
  • Não suportam herança especificada pelo usuário e herdam implicitamente do tipo object

Aula:

  • Valor do tipo de referência
  • Armazenado em pilha
  • Armazene uma referência a um objeto alocado dinamicamente
  • Os construtores são invocados com o operador new, mas isso não aloca memória no heap
  • Várias variáveis ​​podem ter uma referência ao mesmo objeto
  • É possível que operações em uma variável afetem o objeto referenciado pela outra variável

Amostra de código

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();

        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

Saída

O valor inicial do objeto Struct é:10

Método Inside Struct O valor Inside Method de Struct Object é:20

Após a chamada do método, o valor do objeto Struct é:10

O valor inicial do objeto de classe é:10

Método Inside Class O valor do método interno do objeto de classe é:20

Após a chamada do método, o valor do objeto de classe é:20

Aqui você pode ver claramente a diferença entre chamada por valor e chamada por referência.

  1. Os eventos declarados em uma classe têm seus acessos += e -= automaticamente bloqueados por meio de um lock(this) para torná-los thread-safe (eventos estáticos são bloqueados no tipo da classe).Os eventos declarados em uma struct não têm seus acessos += e -= bloqueados automaticamente.Um lock(this) para uma estrutura não funcionaria, pois você só pode bloquear uma expressão de tipo de referência.

  2. A criação de uma instância de struct não pode causar uma coleta de lixo (a menos que o construtor crie direta ou indiretamente uma instância de tipo de referência), enquanto a criação de uma instância de tipo de referência pode causar a coleta de lixo.

  3. Uma estrutura sempre possui um construtor padrão público integrado.

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }
    

    Isso significa que uma estrutura é sempre instanciável, enquanto uma classe pode não ser, já que todos os seus construtores podem ser privados.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Uma estrutura não pode ter um destruidor.Um destruidor é apenas uma substituição de object.Finalize disfarçado, e as estruturas, sendo tipos de valor, não estão sujeitas à coleta de lixo.

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    
    And the CIL for ~Indirect() looks like this:
    
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
    
  5. Uma estrutura é implicitamente selada, uma classe não.
    Uma estrutura não pode ser abstrata, uma classe pode.
    Uma estrutura não pode chamar:base() em seu construtor, enquanto uma classe sem classe base explícita pode.
    Uma estrutura não pode estender outra classe, uma classe pode.
    Uma estrutura não pode declarar membros protegidos (por exemplo, campos, tipos aninhados) que uma classe pode.
    Uma estrutura não pode declarar membros de funções abstratas, uma classe abstrata pode.
    Uma estrutura não pode declarar membros de funções virtuais, uma classe pode.
    Uma estrutura não pode declarar membros de função selados, uma classe pode.
    Uma estrutura não pode declarar membros de função de substituição, uma classe pode.
    A única exceção a esta regra é que uma estrutura pode substituir os métodos virtuais de System.Object, viz, Equals() e GetHashCode() e ToString().

Como mencionado anteriormente:As classes são do tipo referência, enquanto as Structs são tipos de valor com todas as consequências.

Como regra geral, as Diretrizes de Design do Framework recomendam o uso de Structs em vez de classes se:

  • Tem um tamanho de instância inferior a 16 bytes
  • Representa logicamente um único valor, semelhante aos tipos primitivos (int, double, etc.)
  • É imutável
  • Não precisará ser encaixotado com frequência
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                Struct                                                |                                               Class                                               |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type                  | Value-type                                                                                           | Reference-type                                                                                    |
| Where                 | On stack / Inline in containing type                                                                 | On Heap                                                                                           |
| Deallocation          | Stack unwinds / containing type gets deallocated                                                     | Garbage Collected                                                                                 |
| Arrays                | Inline, elements are the actual instances of the value type                                          | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost            | Cheap allocation-deallocation                                                                        | Expensive allocation-deallocation                                                                 |
| Memory usage          | Boxed when cast to a reference type or one of the interfaces they implement,                         | No boxing-unboxing                                                                                |
|                       | Unboxed when cast back to value type                                                                 |                                                                                                   |
|                       | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) |                                                                                                   |
| Assignments           | Copy entire data                                                                                     | Copy the reference                                                                                |
| Change to an instance | Does not affect any of its copies                                                                    | Affect all references pointing to the instance                                                    |
| Mutability            | Should be immutable                                                                                  | Mutable                                                                                           |
| Population            | In some situations                                                                                   | Majority of types in a framework should be classes                                                |
| Lifetime              | Short-lived                                                                                          | Long-lived                                                                                        |
| Destructor            | Cannot have                                                                                          | Can have                                                                                          |
| Inheritance           | Only from an interface                                                                               | Full support                                                                                      |
| Polymorphism          | No                                                                                                   | Yes                                                                                               |
| Sealed                | Yes                                                                                                  | When have sealed keyword                                                                          |
| Constructor           | Can not have explicit parameterless constructors                                                     | Any constructor                                                                                   |
| Null-assignments      | When marked with nullable question mark                                                              | Yes (+ When marked with nullable question mark in C# 8+)                                          |
| Abstract              | No                                                                                                   | When have abstract keyword                                                                        |
| Access Modifiers      | public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

Estruturas são o valor real - elas podem estar vazias, mas nunca nulas

Isso é verdade, no entanto, observe também que a partir do .NET 2 as estruturas suportam uma versão Nullable e o C# fornece algum açúcar sintático para torná-lo mais fácil de usar.

int? value = null;
value  = 1;

Há um caso interessante de quebra-cabeça "class vs struct" - situação em que você precisa retornar vários resultados do método:escolha qual usar.Se você conhece a história do ValueTuple - sabe que ValueTuple (struct) foi adicionado porque deveria ser mais eficaz que Tuple (classe).Mas o que isso significa em números?Dois testes:um é struct/class que possui 2 campos, outro com struct/class que possui 8 campos (com dimensão maior que 4 - a classe deve se tornar mais eficaz que a struct em termos de ticks do processador, mas é claro que a carga do GC também deve ser considerada).

P.S.Outra referência para o caso específico 'sturct ou classe com coleções' está aí: https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

Teste de código:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;

namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }


        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }

        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }

        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }

        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }

        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }

            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }

            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }

            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }

            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }

            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

Cada variável ou campo de um tipo de valor primitivo ou tipo de estrutura contém uma instância única desse tipo, incluindo todos os seus campos (públicos e privados).Por outro lado, variáveis ​​ou campos de tipos de referência podem ser nulos ou podem referir-se a um objeto armazenado em outro lugar, para o qual também pode existir qualquer número de outras referências.Os campos de uma struct serão armazenados no mesmo local que a variável ou campo daquele tipo de estrutura, que pode estar na pilha ou pode ser parte de outro objeto heap.

Criar uma variável ou campo de um tipo de valor primitivo irá criá-lo com um valor padrão;criar uma variável ou campo de um tipo de estrutura criará uma nova instância, criando todos os campos nela da maneira padrão.Criando um novo instância de um tipo de referência começará criando todos os campos nele da maneira padrão e, em seguida, executando código adicional opcional, dependendo do tipo.

Copiar uma variável ou campo de um tipo primitivo para outro copiará o valor.Copiar uma variável ou campo do tipo de estrutura para outro copiará todos os campos (públicos e privados) da primeira instância para a última instância.Copiar uma variável ou campo de tipo de referência para outro fará com que este último se refira à mesma instância que o primeiro (se houver).

É importante observar que em algumas linguagens como C++, o comportamento semântico de um tipo é independente de como ele é armazenado, mas isso não acontece no .NET.Se um tipo implementa semântica de valor mutável, copiar uma variável desse tipo para outra copia as propriedades da primeira para outra instância, referida pela segunda, e usar um membro da segunda para sofrer mutação fará com que a segunda instância seja alterada , mas não o primeiro.Se um tipo implementa semântica de referência mutável, copiar uma variável para outra e usar um membro da segunda para transformar o objeto afetará o objeto referido pela primeira variável;tipos com semântica imutável não permitem mutação, portanto não importa semanticamente se a cópia cria uma nova instância ou cria outra referência à primeira.

No .NET, é possível que os tipos de valor implementem qualquer uma das semânticas acima, desde que todos os seus campos possam fazer o mesmo.Um tipo de referência, entretanto, só pode implementar semântica de referência mutável ou imutável;tipos de valor com campos de tipos de referência mutáveis ​​são limitados à implementação de semântica de referência mutável ou de semântica híbrida estranha.

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