Pregunta

¿Cuál es la diferencia entre estructura y clase en .NET?

¿Fue útil?

Solución

En .NET, hay dos categorías de tipos, tipos de referencia y tipos de valor.

Las estructuras son tipos de valor y las clases son tipos de referencia.

La diferencia general es que un tipo de referencia vive en el montón, y un tipo de valor vive en línea, es decir, dondequiera que esté definida su variable o campo.

Una variable que contiene un tipo de valor contiene toda la tipo de valor valor.Para una estructura, eso significa que la variable contiene la estructura completa, con todos sus campos.

Una variable que contiene un tipo de referencia contiene un puntero o un referencia a algún otro lugar de la memoria donde reside el valor real.

Esto tiene un beneficio, para empezar:

  • tipos de valor siempre contiene un valor
  • tipos de referencia puede contener un nulo-referencia, es decir que no se refieren a nada en absoluto en este momento

Internamente, tipo de referenciaLos s se implementan como punteros y, sabiendo eso y sabiendo cómo funciona la asignación de variables, existen otros patrones de comportamiento:

  • copiar el contenido de un tipo de valor variable en otra variable, copia todo el contenido en la nueva variable, haciendo que las dos sean distintas.En otras palabras, después de la copia, los cambios en uno no afectarán al otro.
  • copiar el contenido de un tipo de referencia variable en otra variable, copia la referencia, lo que significa que ahora tiene dos referencias a la misma en algún otro lugar almacenamiento de los datos reales.En otras palabras, después de la copia, cambiar los datos en una referencia parecerá afectar a la otra también, pero solo porque en realidad estás viendo los mismos datos en ambos lugares.

Cuando declaras variables o campos, así es como se diferencian los dos tipos:

  • variable: tipo de valor vive en la pila, tipo de referencia vive en la pila como un puntero a algún lugar de la memoria del montón donde vive la memoria real (aunque tenga en cuenta Serie de artículos de Eric Lipperts:La pila es un detalle de implementación.)
  • clase/campo de estructura: tipo de valor vive completamente dentro del tipo, tipo de referencia vive dentro del tipo como un puntero a algún lugar de la memoria del montón donde vive la memoria real.

Otros consejos

Un breve resumen de cada uno:

Sólo clases:

  • Puede soportar la herencia
  • Son tipos de referencia (puntero).
  • La referencia puede ser nula.
  • Tener sobrecarga de memoria por nueva instancia

Sólo estructuras:

  • No se puede soportar la herencia
  • Son tipos de valor
  • Se pasan por valor (como números enteros)
  • No puede tener una referencia nula (a menos que se utilice Nullable)
  • No tenga una sobrecarga de memoria por cada nueva instancia, a menos que esté 'encajonada'

Tanto clases como estructuras:

  • ¿Los tipos de datos compuestos se utilizan normalmente para contener algunas variables que tienen alguna relación lógica?
  • Puede contener métodos y eventos.
  • Puede soportar interfaces

En .NET, las declaraciones de estructura y clase diferencian entre tipos de referencia y tipos de valor.

Cuando pasa un tipo de referencia, en realidad solo hay uno almacenado.Todo el código que accede a la instancia está accediendo a la misma.

Cuando pasas un tipo de valor, cada uno es una copia.Todo el código funciona en su propia copia.

Esto se puede demostrar con un ejemplo:

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 una clase esto sería 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.

Las clases no pueden ser nada: la referencia puede apuntar a un valor nulo.

Las estructuras son el valor real; pueden estar vacías pero nunca nulas.Por esta razón, las estructuras siempre tienen un constructor predeterminado sin parámetros; necesitan un "valor inicial".

De Microsoft Elegir entre clase y estructura ...

Como regla general, la mayoría de los tipos en un marco deben ser clases.Sin embargo, hay algunas situaciones en las que las características de un tipo de valor hacen que sea más apropiado usar estructuras.

CONSIDERE una estructura en lugar de una clase:

  • Si las instancias de este tipo son pequeñas y comúnmente de corta duración o comúnmente están incrustadas en otros objetos.

X EVITE una estructura a menos que el tipo tenga todo de las siguientes características:

  • Lógicamente representa un valor único, similar a los tipos primitivos (int, double, etc.).
  • Tiene un tamaño de instancia inferior a 16 bytes.
  • Es inmutable. (no puede ser cambiado)
  • No será necesario encajonarlo con frecuencia.

Además de todas las diferencias descritas en las otras respuestas:

  1. estructuras no puede tener un constructor explícito sin parámetros mientras que una clase puede
  2. estructuras no puede tener destructores, mientras que una clase puede
  3. estructuras no puedo heredar de otra estructura o clase, mientras que una clase puede heredar de otra clase.(Tanto las estructuras como las clases se pueden implementar desde una interfaz).

Si buscas un vídeo que explique todas las diferencias, puedes consultar Parte 29 - Tutorial de C# - Diferencia entre clases y estructuras en C#.

Las instancias de clases se almacenan en el montón administrado.Todas las variables que 'contienen' una instancia son simplemente una referencia a la instancia en el montón.Pasar un objeto a un método da como resultado que se pase una copia de la referencia, no el objeto en sí.

Las estructuras (técnicamente, tipos de valores) se almacenan dondequiera que se utilicen, de forma muy parecida a un tipo primitivo.El tiempo de ejecución puede copiar el contenido en cualquier momento y sin invocar un constructor de copia personalizado.Pasar un tipo de valor a un método implica copiar el valor completo, nuevamente sin invocar ningún código personalizable.

La distinción se mejora con los nombres de C++/CLI:"clase de referencia" es una clase como se describe primero, "clase de valor" es una clase como se describe en segundo lugar.Las palabras clave "clase" y "estructura" tal como las usa C# son simplemente algo que debe aprenderse.

Diferencia entre estructuras y clases:

  • Las estructuras son de tipo valor. mientras Las clases son de tipo referencia..
  • Las estructuras se almacenan en la pila. mientras Las clases se almacenan en el montón.
  • Los tipos de valor contienen su valor en la memoria donde se declaran, pero el tipo de referencia contiene una referencia a una memoria de objeto.
  • Tipos de valor destruidos inmediatamente Después de que se pierde el alcance, mientras que el tipo de referencia solo la destrucción variable después del alcance se pierde.El objeto es luego destruido por el recolector de basura.
  • Cuando copia estructura en otra estructura, una nueva copia de esa estructura se crea modificada de una estructura no afectará el valor de la otra estructura.
  • Cuando copia una clase en otra clase, solo copia la variable de referencia.
  • Ambas variables de referencia apuntan al mismo objeto en el montón.El cambio a una variable afectará a la otra variable de referencia.
  • Las estructuras no pueden tener destructores., pero las clases pueden tener destructores.
  • Las estructuras no pueden tener constructores explícitos sin parámetros. Mientras que una clase puede estructuras no es compatible con la herencia, pero las clases sí.Ambos admiten la herencia de una interfaz.
  • Las estructuras son de tipo sellado..

Estructura versus clase

Una estructura es un tipo de valor, por lo que se almacena en la pila, pero una clase es un tipo de referencia y se almacena en el montón.

Una estructura no admite herencia ni polimorfismo, pero una clase admite ambos.

De forma predeterminada, todos los miembros de la estructura son públicos, pero los miembros de la clase son de naturaleza privada de forma predeterminada.

Como una estructura es un tipo de valor, no podemos asignar nulo a un objeto de estructura, pero no es el caso de una clase.

Para completarlo, hay otra diferencia al usar el Equals método, que es heredado por todas las clases y estructuras.

Digamos que tenemos una clase y una estructura:

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

y en el método Main tenemos 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;
}

Entonces:

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

Entonces, las estructuras son adecuadas para objetos de tipo numérico, como puntos (guarde las coordenadas xey).Y las clases son adecuadas para otros.Incluso si 2 personas tienen el mismo nombre, altura, peso..., siguen siendo 2 personas.

Para agregar a las otras respuestas, hay una diferencia fundamental que vale la pena señalar, y es cómo se almacena en la memoria.Esto puede tener un efecto importante en el rendimiento de las matrices.Las estructuras son tipos de valor, por lo que almacenan un valor en el área de memoria a la que apuntan, las clases son tipos de referencia, por lo que hacen referencia a una clase en el área de memoria a la que apuntan, el valor real se almacena en otro lugar.

  • Con una estructura, la memoria se asigna dentro de la clase contenedora para almacenar los datos.
  • Con una clase, la clase que la contiene solo contendrá un puntero a la nueva clase en un área diferente de la memoria.

Esto también es cierto con las matrices, por lo que una matriz de estructuras se ve así en la memoria.

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

Donde una variedad de clases se ve así

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

Los valores reales que le interesan no se almacenan en la matriz, sino en otra parte de la memoria.

Para la gran mayoría de aplicaciones, esta diferencia realmente no importa; sin embargo, en código de alto rendimiento esto afectará la localidad de los datos dentro de la memoria y tendrá un gran impacto en el rendimiento de la memoria caché de la CPU.El uso de clases cuando podría/debería haber usado estructuras aumentará enormemente la cantidad de errores de caché en la CPU.

Lo más lento que hace una CPU moderna no es hacer números, sino recuperar datos de la memoria, y un acierto en la caché L1 es muchas veces más rápido que leer datos de la RAM.

Bueno, para empezar, una estructura se pasa por valor en lugar de por referencia.Las estructuras son buenas para estructuras de datos relativamente simples, mientras que las clases tienen mucha más flexibilidad desde un punto de vista arquitectónico a través del polimorfismo y la herencia.

Probablemente otros puedan brindarle más detalles que yo, pero uso estructuras cuando la estructura que busco es simple.

Además de la diferencia básica del especificador de acceso, y algunas de las mencionadas anteriormente, me gustaría agregar algunas de las diferencias principales, incluidas algunas de las mencionadas anteriormente, con un ejemplo de código con salida, que dará una idea más clara de la referencia y el valor.

Estructuras:

  • Son tipos de valor y no requieren asignación de montón.
  • La asignación de memoria es diferente y se almacena en la pila.
  • Útil para estructuras de datos pequeñas.
  • Afecta el rendimiento, cuando pasamos valor al método, pasamos toda la estructura de datos y todo se pasa a la pila.
  • El constructor simplemente devuelve el valor de la estructura en sí (generalmente en una ubicación temporal en la pila) y luego este valor se copia según sea necesario.
  • Cada una de las variables tiene su propia copia de los datos y no es posible que las operaciones en una afecten a la otra.
  • No admiten la herencia especificada por el usuario y heredan implícitamente del tipo de objeto.

Clase:

  • Valor del tipo de referencia
  • Almacenado en montón
  • Almacenar una referencia a un objeto asignado dinámicamente
  • Los constructores se invocan con el operador new, pero eso no asigna memoria en el montón
  • Varias variables pueden tener una referencia al mismo objeto.
  • Es posible que las operaciones en una variable afecten al objeto al que hace referencia la otra variable.

Ejemplo 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;
        }
    }

Producción

El valor inicial del objeto Struct es:10

Método interno de estructura El valor del método interno del objeto struct es:20

Después de la llamada al método, el valor del objeto Struct es:10

El valor inicial del objeto de clase es:10

Método de clase interna El valor del método interno del objeto de clase es:20

Después de la llamada al método, el valor del objeto de clase es:20

Aquí puede ver claramente la diferencia entre llamada por valor y llamada por referencia.

  1. Los eventos declarados en una clase tienen su acceso += y -= bloqueado automáticamente mediante un candado (this) para hacerlos seguros para subprocesos (los eventos estáticos están bloqueados según el tipo de clase).Los eventos declarados en una estructura no tienen su acceso += y -= bloqueado automáticamente.Un bloqueo (this) para una estructura no funcionaría ya que solo se puede bloquear una expresión de tipo de referencia.

  2. La creación de una instancia de estructura no puede causar una recolección de basura (a menos que el constructor cree directa o indirectamente una instancia de tipo de referencia), mientras que la creación de una instancia de tipo de referencia puede causar recolección de basura.

  3. Una estructura siempre tiene un constructor público predeterminado incorporado.

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

    Esto significa que una estructura siempre es instanciable, mientras que una clase puede no serlo, ya que todos sus constructores pueden ser privados.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Una estructura no puede tener un destructor.Un destructor es simplemente una anulación de un objeto. Finaliza disfrazado y las estructuras, al ser tipos de valor, no están sujetas a la recolección de basura.

    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. Una estructura está implícitamente sellada, una clase no.
    Una estructura no puede ser abstracta, una clase sí.
    Una estructura no puede llamar:base() en su constructor, mientras que una clase sin una clase base explícita sí puede hacerlo.
    Una estructura no puede extender otra clase, una clase sí.
    Una estructura no puede declarar miembros protegidos (por ejemplo, campos, tipos anidados), pero una clase sí.
    Una estructura no puede declarar miembros de funciones abstractas, una clase abstracta sí.
    Una estructura no puede declarar miembros de funciones virtuales, una clase sí.
    Una estructura no puede declarar miembros de funciones selladas, una clase sí.
    Una estructura no puede declarar miembros de función de anulación, una clase sí.
    La única excepción a esta regla es que una estructura puede anular los métodos virtuales de System.Object, a saber, Equals(), GetHashCode() y ToString().

Como se menciono antes:Las clases son de tipo referencia, mientras que las estructuras son tipos de valor con todas las consecuencias.

Como regla general, las Pautas de diseño de marcos recomiendan usar estructuras en lugar de clases si:

  • Tiene un tamaño de instancia inferior a 16 bytes.
  • Lógicamente representa un valor único, similar a los tipos primitivos (int, double, etc.)
  • es inmutable
  • No será necesario encajonarlo con frecuencia.
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                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                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

Las estructuras son el valor real: pueden estar vacías pero nunca nulas

Esto es cierto, sin embargo, también tenga en cuenta que a partir de .NET 2 las estructuras admiten una versión anulable y C# proporciona algo de azúcar sintáctico para que sea más fácil de usar.

int? value = null;
value  = 1;

Hay un caso interesante de rompecabezas "clase vs estructura": situación en la que es necesario devolver varios resultados del método:elegir cuál usar.Si conoce la historia de ValueTuple, sabrá que se agregó ValueTuple (estructura) porque debería ser más efectivo que Tuple (clase).Pero ¿qué significa en números?Dos pruebas:uno es estructura/clase que tiene 2 campos, otro con estructura/clase que tiene 8 campos (con dimensiones superiores a 4; la clase debería volverse más efectiva que la estructura en términos de tics del procesador, pero, por supuesto, también se debe considerar la carga de GC).

PDOtro punto de referencia para casos específicos 'estructura o clase con colecciones' está ahí: 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 |

Prueba 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 variable o campo de un tipo de valor primitivo o tipo de estructura contiene una instancia única de ese tipo, incluidos todos sus campos (públicos y privados).Por el contrario, las variables o campos de tipos de referencia pueden contener valores nulos o pueden hacer referencia a un objeto almacenado en otro lugar, al que también pueden existir otras referencias.Los campos de una estructura se almacenarán en el mismo lugar que la variable o campo de ese tipo de estructura, que puede estar en la pila o puede estar parte de otro objeto del montón.

Crear una variable o campo de un tipo de valor primitivo lo creará con un valor predeterminado;La creación de una variable o campo de un tipo de estructura creará una nueva instancia, creando todos los campos en ella de la manera predeterminada.Creando un nuevo instancia de un tipo de referencia comenzará creando todos los campos allí de la manera predeterminada y luego ejecutando código adicional opcional dependiendo del tipo.

Copiar una variable o campo de un tipo primitivo a otro copiará el valor.Copiar una variable o campo de tipo de estructura a otra copiará todos los campos (públicos y privados) de la primera instancia a la última instancia.Copiar una variable o campo de tipo de referencia a otro hará que este último haga referencia a la misma instancia que el primero (si corresponde).

Es importante tener en cuenta que en algunos lenguajes como C++, el comportamiento semántico de un tipo es independiente de cómo se almacena, pero eso no se aplica a .NET.Si un tipo implementa una semántica de valores mutables, copiar una variable de ese tipo a otra copia las propiedades de la primera a otra instancia, a la que hace referencia la segunda, y usar un miembro de la segunda para mutar hará que se cambie esa segunda instancia. , pero no el primero.Si un tipo implementa una semántica de referencia mutable, copiar una variable a otra y usar un miembro de la segunda para mutar el objeto afectará el objeto al que hace referencia la primera variable;Los tipos con semántica inmutable no permiten la mutación, por lo que no importa semánticamente si la copia crea una nueva instancia o crea otra referencia a la primera.

En .NET, es posible que los tipos de valor implementen cualquiera de las semánticas anteriores, siempre que todos sus campos puedan hacer lo mismo.Sin embargo, un tipo de referencia sólo puede implementar una semántica de referencia mutable o una semántica inmutable;Los tipos de valor con campos de tipos de referencia mutables se limitan a implementar una semántica de referencia mutable o una semántica híbrida extraña.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top