2012-02-09 11 views
52

En C#, las estructuras se gestionan en términos de valores y los objetos están en referencia. Desde mi entender, al crear una instancia de una clase, la palabra clave new causa C# para usar la información de clase para hacer el caso, como en a continuación:¿Qué significa la palabra clave "nuevo" para una estructura en C#?

class MyClass 
{ 
    ... 
} 
MyClass mc = new MyClass(); 

Por estructura, no se va a crear un objeto, sino simplemente establecer una variable a un valor:

struct MyStruct 
{ 
    public string name; 
} 
MyStruct ms; 
//MyStruct ms = new MyStruct();  
ms.name = "donkey"; 

lo que no entiendo es si declarar variables por MyStruct ms = new MyStruct(), lo que es la palabra clave aquí new está haciendo a la declaración? . Si struct no puede ser un objeto, ¿cuál es el new aquí instanciando?

+2

Una instancia de una 'struct' * es * un objeto. La distinción que probablemente malinterprete es la que existe entre los tipos de valores y los tipos de referencia. –

+0

pero en C no hay objeto y struct no es un objeto. Entonces, ¿en C# struct se implementa como objeto? – KMC

+1

Pensar en C# en términos de C no es útil. Ignora las diferencias sintácticas, son idiomas completamente diferentes. –

Respuesta

43

De struct (C# Reference) en MSDN:

When you create a struct object using the new operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the new operator. If you do not use new, the fields will remain unassigned and the object cannot be used until all of the fields are initialized.

A mi entender, en realidad no se podrá utilizar una estructura adecuada sin usar nuevo a menos que asegúrese de inicializar todos los campos manualmente. Si usa el nuevo operador, el constructor hará esto por usted.

Espero que lo aclare. Si necesita una aclaración sobre esto, hágamelo saber.


Editar

Hay un largo hilo de comentarios, así que pensé que me gustaría añadir un poco más aquí. Creo que la mejor manera de entenderlo es intentarlo. Haga un proyecto de consola en Visual Studio llamado "StructTest" y copie el siguiente código en él.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace struct_test 
{ 
    class Program 
    { 
     public struct Point 
     { 
      public int x, y; 

      public Point(int x) 
      { 
       this.x = x; 
       this.y = 5; 
      } 

      public Point(int x, int y) 
      { 
       this.x = x; 
       this.y = y; 
      } 

      // It will break with this constructor. If uncommenting this one 
      // comment out the other one with only one integer, otherwise it 
      // will fail because you are overloading with duplicate parameter 
      // types, rather than what I'm trying to demonstrate. 
      /*public Point(int y) 
      { 
       this.y = y; 
      }*/ 
     } 

     static void Main(string[] args) 
     { 
      // Declare an object: 
      Point myPoint; 
      //Point myPoint = new Point(10, 20); 
      //Point myPoint = new Point(15); 
      //Point myPoint = new Point(); 


      // Initialize: 
      // Try not using any constructor but comment out one of these 
      // and see what happens. (It should fail when you compile it) 
      myPoint.x = 10; 
      myPoint.y = 20; 

      // Display results: 
      Console.WriteLine("My Point:"); 
      Console.WriteLine("x = {0}, y = {1}", myPoint.x, myPoint.y); 

      Console.ReadKey(true); 
     } 
    } 
} 

Juega con ella. Eliminar los constructores y ver qué pasa. Intenta usar un constructor que solo inicialice una variable (he comentado una ... no se compilará). Pruebe con y sin la nueva palabra clave (he comentado algunos ejemplos, los eliminó y los puso a prueba).

+0

"las estructuras se pueden crear instancias"? pero struct no puede ser un objeto, ¿verdad? ¿Son los "campos" en la estructura las propiedades y los métodos? Si struct no es un objeto, ¿por qué sus campos deben inicializarse? Creo que necesito más aclaración. Gracias. – KMC

+0

¿Por qué * no * los campos de la estructura deben inicializarse, si no se llama a un constructor? Si no los inicializa y no llama a un constructor para inicializarlos, no se inicializan. – hvd

+0

Una estructura es un tipo de objeto. Descrito en el sitio web de Microsoft como un "objeto liviano". Puede tener variables, pero no funciones. Una estructura es como una clase, pero todos los miembros son públicos y no puedes tener ninguna función. Le permite almacenar información, pero no puede manipular o controlar esa información como puede en una clase. Podría hacer una estructura "nueva" para usar la misma variable pero borrar todos los datos. – joshhendo

3

Usar "nuevo MyStuct()" asegura que todos los campos estén configurados en algún valor. En el caso anterior, nada es diferente. Si en lugar de establecer ms.name trataras de leerlo, obtendrías un error de "Uso del posible campo no asignado 'nombre'" en VS.

3

Cada vez que un objeto o estructura aparece, todos sus campos aparecen también; si alguno de esos campos son tipos de estructuras, todos los campos anidados también existen. Cuando se crea una matriz, todos sus elementos entran en existencia (y, como anteriormente, si alguno de esos elementos son estructuras, los campos de esas estructuras también entran en existencia). Todo esto ocurre antes de que cualquier código de constructor tenga la oportunidad de ejecutarse.

En .net, un constructor de estructuras no es más que un método que toma una estructura como un parámetro 'fuera'. En C#, una expresión que llama a un constructor de estructura asignará una instancia de estructura temporal, llamará al constructor sobre eso y luego usará esa instancia temporal como el valor de la expresión. Tenga en cuenta que esto es diferente de vb.net, donde el código generado para un constructor comenzará poniendo a cero todos los campos, pero donde el código de la persona que llama intentará que el constructor opere directamente sobre el destino. Por ejemplo: myStruct = new myStructType(whatever) en vb.net borrará myStruct antes de que se ejecute la primera declaración del constructor; dentro del constructor, cualquier escritura en el objeto en construcción operará inmediatamente al myStruct.

0

ValueType y las estructuras son algo especial en C#. Aquí te estoy mostrando lo que sucede cuando nuevo algo.

Aquí tenemos el siguiente

  • Código

    partial class TestClass { 
        public static void NewLong() { 
         var i=new long(); 
        } 
    
        public static void NewMyLong() { 
         var i=new MyLong(); 
        } 
    
        public static void NewMyLongWithValue() { 
         var i=new MyLong(1234); 
        } 
    
        public static void NewThatLong() { 
         var i=new ThatLong(); 
        } 
    } 
    
    [StructLayout(LayoutKind.Sequential)] 
    public partial struct MyLong { 
        const int bits=8*sizeof(int); 
    
        public static implicit operator int(MyLong x) { 
         return (int)x.m_Low; 
        } 
    
        public static implicit operator long(MyLong x) { 
         long y=x.m_Hi; 
         return (y<<bits)|x.m_Low; 
        } 
    
        public static implicit operator MyLong(long x) { 
         var y=default(MyLong); 
         y.m_Low=(uint)x; 
         y.m_Hi=(int)(x>>bits); 
         return y; 
        } 
    
        public MyLong(long x) { 
         this=x; 
        } 
    
        uint m_Low; 
        int m_Hi; 
    } 
    
    public partial class ThatLong { 
        const int bits=8*sizeof(int); 
    
        public static implicit operator int(ThatLong x) { 
         return (int)x.m_Low; 
        } 
    
        public static implicit operator long(ThatLong x) { 
         long y=x.m_Hi; 
         return (y<<bits)|x.m_Low; 
        } 
    
        public static implicit operator ThatLong(long x) { 
         return new ThatLong(x); 
        } 
    
        public ThatLong(long x) { 
         this.m_Low=(uint)x; 
         this.m_Hi=(int)(x>>bits); 
        } 
    
        public ThatLong() { 
         int i=0; 
         var b=i is ValueType; 
        } 
    
        uint m_Low; 
        int m_Hi; 
    } 
    

Y la IL generada de los métodos de la clase de prueba sería

  • IL

    // NewLong 
    .method public hidebysig static 
        void NewLong() cil managed 
    { 
        .maxstack 1 
        .locals init (
         [0] int64 i 
        ) 
    
        IL_0000: nop 
        IL_0001: ldc.i4.0 // push 0 as int 
        IL_0002: conv.i8 // convert the pushed value to long 
        IL_0003: stloc.0 // pop it to the first local variable, that is, i 
        IL_0004: ret 
    } 
    
    // NewMyLong 
    .method public hidebysig static 
        void NewMyLong() cil managed 
    { 
        .maxstack 1 
        .locals init (
         [0] valuetype MyLong i 
        ) 
    
        IL_0000: nop 
        IL_0001: ldloca.s i  // push address of i 
        IL_0003: initobj MyLong // pop address of i and initialze as MyLong 
        IL_0009: ret 
    } 
    
    // NewMyLongWithValue 
    .method public hidebysig static 
        void NewMyLongWithValue() cil managed 
    { 
        .maxstack 2 
        .locals init (
         [0] valuetype MyLong i 
        ) 
    
        IL_0000: nop 
        IL_0001: ldloca.s i // push address of i 
        IL_0003: ldc.i4 1234 // push 1234 as int 
        IL_0008: conv.i8  // convert the pushed value to long 
    
        // call the constructor 
        IL_0009: call instance void MyLong::.ctor(int64) 
    
        IL_000e: nop 
        IL_000f: ret 
    } 
    
    // NewThatLong 
    .method public hidebysig static 
        void NewThatLong() cil managed 
    { 
        // Method begins at RVA 0x33c8 
        // Code size 8 (0x8) 
        .maxstack 1 
        .locals init (
         [0] class ThatLong i 
        ) 
    
        IL_0000: nop 
    
        // new by calling the constructor and push it's reference 
        IL_0001: newobj instance void ThatLong::.ctor() 
    
        // pop it to the first local variable, that is, i 
        IL_0006: stloc.0 
    
        IL_0007: ret 
    } 
    

El comportamiento de los métodos se comentó en el código IL. Y es posible que desee echar un vistazo a OpCodes.Initobj y OpCodes.Newobj. El tipo de valor generalmente se inicializa con OpCodes.Initobj, pero como dice MSDN, también se usará OpCodes.Newobj.

  • descripción en OpCodes.Newobj

    Value types are not usually created using newobj. They are usually allocated either as arguments or local variables, using newarr (for zero-based, one-dimensional arrays), or as fields of objects. Once allocated, they are initialized using Initobj. However, the newobj instruction can be used to create a new instance of a value type on the stack, that can then be passed as an argument, stored in a local, and so on.

Para cada tipo de valor que es numérico, byte-double, tiene un op-código definido. Aunque se declaran como struct, hay alguna diferencia en el IL generado como se muestra.

Aquí hay dos cosas más para mencionar:

  1. ValueType sí se declara una clase abstracta

    Es decir, no se puede nueva directamente.

  2. struct s no pueden contener constructores sin parámetros explícitos

    Es decir, cuando se nueva un struct, se caería en el caso anterior de cualquiera NewMyLong o NewMyLongWithValue.

Para resumir, nueva de los tipos de valores y las estructuras son para la consistencia del concepto del lenguaje.

12

Catch Eric Lippert's excellent answer from this thread. citarle:

When you "new" a value type, three things happen. First, the memory manager allocates space from short term storage. Second, the constructor is passed a reference to the short term storage location. After the constructor runs, the value that was in the short-term storage location is copied to the storage location for the value, wherever that happens to be. Remember, variables of value type store the actual value.

(Note that the compiler is allowed to optimize these three steps into one step if the compiler can determine that doing so never exposes a partially-constructed struct to user code. That is, the compiler can generate code that simply passes a reference to the final storage location to the constructor, thereby saving one allocation and one copy.)

(haciendo esta respuesta, ya que realmente es una)

+0

Vale la pena observar que debido a que los parámetros 'out' son un concepto de C#, en lugar de uno utilizado por .NET Runtime, pasar una estructura parcialmente construida como un parámetro' out' a un método externo expondrá sus valores a un código externo. a pesar de que el compilador C# supondrá que no. Uno podría, por ejemplo, definir una estructura de tal manera que 'myThing = newmyThing (5);' inicializará un campo de 'myThing' mientras que los otros no se verán afectados. – supercat

+0

Si alguien puede vencer a Eric, esa es una declaración. Bien hecho ... – nawfal

+2

Percibo que los diferentes grupos de idiomas probablemente tienen su propia visión de lo que debe ser .NET, y pretenden que se ajusta a su visión. Por ejemplo, el grupo C# probablemente calcule que .NET * debería * tener parámetros ejecutables 'out', y si todos programaran en C# lo haría, pero un método virtual con un parámetro' out' será considerado por otros lenguajes como un método virtual con un parámetro 'ref'. En algunos casos, es agradable que los idiomas no estén limitados al subconjunto mínimo de funciones que otros implementadores de lenguaje pueden querer implementar, pero también existen peligros. – supercat

-1

En una estructura, la palabra clave es new innecesariamente confuso. No hace nada. Solo se requiere si quieres usar el constructor. Hace no realiza un new.

El significado habitual de new es asignar el almacenamiento permanente (en el montón.) un lenguaje como C++ permite new myObject() o simplemente myObject(). Ambos llaman al mismo constructor. Pero el primero crea un nuevo objeto y devuelve un puntero. Este último simplemente crea una temperatura. Cualquier estructura o clase puede usar cualquiera. new es una elección, y significa algo.

C# no le permite elegir. Las clases siempre están en el montón, y las estructuras siempre están en la pila. No es posible realizar un new real en una estructura. Los programadores experimentados de C# están acostumbrados a esto. Cuando ven ms = new MyStruct(); saben ignorar el new como una simple sintaxis. Saben que actúa como ms = MyStruct(), que simplemente asigna a un objeto existente.

Curiosamente (?), Las clases requieren el new. c=myClass(); no está permitido (utilizando el constructor para establecer los valores del objeto existente c). Tendría que hacer algo como c.init();. Por lo tanto, nunca tiene una opción: los constructores siempre asignan para las clases y nunca para las estructuras. El new siempre es solo una decoración.

supongo que la razón por la que requiere falsos new 's en estructuras es tan usted puede cambiar fácilmente una estructura en una clase (suponiendo que siempre utilice myStruct=new myStruct(); primera vez que se declara, que se recomienda.)

+0

Pensar en términos de detalles de implementación es incorrecto, las estructuras no siempre se asignan en la pila, p. ¡Estructura campos en una clase, boxeo, etc. ... y lo nuevo realmente hace algo! –

+0

La confusión es que las personas están leyendo esta pregunta como "¿por qué debería usar las nuevas con estructuras?" Pero si lo lees, la pregunta es realmente la última frase "¿qué es lo nuevo aquí instaurando?" Es una pregunta sobre por qué eligen esa sintaxis divertida. –

Cuestiones relacionadas