que - ¿ en c# qué es método?
¿{} Actúa como() al crear un nuevo objeto en C#? (7)
Acabo de notar que usar
{}
lugar de
()
da los mismos resultados al construir un objeto.
class Customer
{
public string name;
public string ID {get; set;}
}
static void Main()
{
Customer c1= new Customer{}; //Is this a constructor?
Customer c2= new Customer();
//what is the concept behind the ability to assign values for properties
//and fields inside the {} and is not allowable to do it inside ()
//without defining a constructor:
Customer c3= new Customer{name= "John", ID="ABC"};
}
¿
{}
Actúa como
()
al crear un nuevo objeto en C #?
Puede usar inicializadores de objetos para inicializar objetos de tipo de manera declarativa sin invocar explícitamente un constructor para el tipo.
https://msdn.microsoft.com/en-us/library/bb397680.aspx
También puede omitir llamar al constructor si el tipo tiene un constructor predeterminado. Entonces
Customer c1 = new Customer { };
es exactamente lo mismo que
Customer c1 = new Customer() { };
En su escenario específico, sí, produciría el mismo resultado, pero no por una razón que probablemente piense.
Para entender el resultado, supongamos que tiene una clase como esta:
class Customer
{
public string name;
public string ID {get; set;}
public Customer()
{
}
public Customer(string n, string id)
{
name = n;
ID = id;
}
}
Cuando lo creas así:
Customer c = new Customer("john", "someID");
Llama al segundo constructor y le dice a la clase que está pasando estos valores y que la responsabilidad de hacer lo que cree que es lo mejor recae en el constructor (en este caso usaría estos valores para pasarlos a los campos públicos).
Cuando lo creas así:
Customer c = new Customer { name = "john", ID = "someID" };
Estás configurando los campos públicos por ti mismo y usas el constructor vacío.
De cualquier manera, debe EVITAR el uso de campos públicos, porque no es seguro. No debe permitir que nadie de fuera los modifique directamente así. En su lugar, use solo campos privados y use propiedades públicas para administrar el acceso desde el exterior.
Hay tres formas de crear directamente un nuevo objeto en C #:
-
Una llamada de constructor simple con una lista de argumentos:
new Foo() // Empty argument list new Foo(10, 20) // Passing arguments
-
Un inicializador de objeto con una lista de argumentos
new Foo() { Name = "x" } // Empty argument list new Foo(10, 20) { Name = "x" } // Two arguments
-
Un inicializador de objeto sin lista de argumentos
new Foo { Name = "x" }
La última forma es exactamente equivalente a especificar una lista de argumentos vacía. Por lo general, llamará a un constructor sin parámetros, pero podría llamar a un constructor donde todos los parámetros tienen valores predeterminados.
Ahora, en los dos ejemplos de inicializador de objeto que he dado, establecí una propiedad de
Name
, y podría establecer otras propiedades / campos, o incluso establecer
ninguna
propiedad y campo.
Entonces, los tres son equivalentes, efectivamente no pasan argumentos de constructor y no especifican propiedades / campos para establecer:
new Foo()
new Foo() {}
new Foo {}
De estos, el primero es el más convencional.
Ninguna nueva versión de C # crea implícitamente un Constructor para la inicialización de objetos
Customer c1= new Customer{};
Arriba es lo mismo que
Customer c1= new Customer()
{
};
Inicializadores de objetos y colecciones (Guía de programación de C #)
()
- llama al constructor sin parámetros.
{}
- se supone que se usa para asignar propiedades.
Usar
{}
sin
()
es un acceso directo y funcionará mientras exista un constructor sin parámetros.
Customer c1= new Customer{}
: este es el inicializador de propiedades.
Puedes escribirlo como:
Customer c1 = new Customer{
name="some text",
ID="some id"
};
Customer c1 = new Customer {};
Este es un inicializador de objeto vacío. Según la spec , los inicializadores de objetos llamarán al constructor predeterminado a menos que usted especifique el constructor a usar. Como no se realiza la inicialización, se compilará de la misma manera que con el constructor predeterminado.
Para responder la pregunta, si ''
{}
actúa [s] like
()
al crear un nuevo objeto en C #'', tenemos que entrar en más detalles.
Por lo que le importa, los objetos resultantes contendrán los mismos datos, pero el IL generado no es idéntico.
El siguiente ejemplo
namespace SO28254462
{
class Program
{
class Customer
{
private readonly Foo foo = new Foo();
public string name;
public Customer()
{
}
public Customer(string id)
{
this.ID = id;
}
public string ID { get; set; }
public Foo Foo
{
get
{
return this.foo;
}
}
}
class Foo
{
public string Bar { get; set; }
}
static void Main(string[] args)
{
Customer c1 = new Customer { };
Customer c2 = new Customer();
Customer c3 = new Customer { name = "John", ID = "ABC", Foo = { Bar = "whatever" } };
Customer c4 = new Customer();
c4.name = "John";
c4.ID = "ABC";
c4.Foo.Bar = "whatever";
Customer c5 = new Customer("ABC") { name = "John", Foo = { Bar = "whatever" } };
Customer c6 = new Customer("ABC");
c6.name = "John";
c6.Foo.Bar = "whatever";
}
}
}
compilará a este IL (solo método principal, depuración sin optimizaciones)
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 201 (0xc9)
.maxstack 2
.entrypoint
.locals init (
[0] class SO28254462.Program/Customer c1,
[1] class SO28254462.Program/Customer c2,
[2] class SO28254462.Program/Customer c3,
[3] class SO28254462.Program/Customer c4,
[4] class SO28254462.Program/Customer c5,
[5] class SO28254462.Program/Customer c6,
[6] class SO28254462.Program/Customer ''<>g__initLocal0'',
[7] class SO28254462.Program/Customer ''<>g__initLocal1''
)
IL_0000: nop
IL_0001: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0006: stloc.0
IL_0007: newobj instance void SO28254462.Program/Customer::.ctor()
IL_000c: stloc.1
IL_000d: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0012: stloc.s ''<>g__initLocal0''
IL_0014: ldloc.s ''<>g__initLocal0''
IL_0016: ldstr "John"
IL_001b: stfld string SO28254462.Program/Customer::name
IL_0020: ldloc.s ''<>g__initLocal0''
IL_0022: ldstr "ABC"
IL_0027: callvirt instance void SO28254462.Program/Customer::set_ID(string)
IL_002c: nop
IL_002d: ldloc.s ''<>g__initLocal0''
IL_002f: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_0034: ldstr "whatever"
IL_0039: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_003e: nop
IL_003f: ldloc.s ''<>g__initLocal0''
IL_0041: stloc.2
IL_0042: newobj instance void SO28254462.Program/Customer::.ctor()
IL_0047: stloc.3
IL_0048: ldloc.3
IL_0049: ldstr "John"
IL_004e: stfld string SO28254462.Program/Customer::name
IL_0053: ldloc.3
IL_0054: ldstr "ABC"
IL_0059: callvirt instance void SO28254462.Program/Customer::set_ID(string)
IL_005e: nop
IL_005f: ldloc.3
IL_0060: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_0065: ldstr "whatever"
IL_006a: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_006f: nop
IL_0070: ldstr "ABC"
IL_0075: newobj instance void SO28254462.Program/Customer::.ctor(string)
IL_007a: stloc.s ''<>g__initLocal1''
IL_007c: ldloc.s ''<>g__initLocal1''
IL_007e: ldstr "John"
IL_0083: stfld string SO28254462.Program/Customer::name
IL_0088: ldloc.s ''<>g__initLocal1''
IL_008a: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_008f: ldstr "whatever"
IL_0094: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_0099: nop
IL_009a: ldloc.s ''<>g__initLocal1''
IL_009c: stloc.s c5
IL_009e: ldstr "ABC"
IL_00a3: newobj instance void SO28254462.Program/Customer::.ctor(string)
IL_00a8: stloc.s c6
IL_00aa: ldloc.s c6
IL_00ac: ldstr "John"
IL_00b1: stfld string SO28254462.Program/Customer::name
IL_00b6: ldloc.s c6
IL_00b8: callvirt instance class SO28254462.Program/Foo SO28254462.Program/Customer::get_Foo()
IL_00bd: ldstr "whatever"
IL_00c2: callvirt instance void SO28254462.Program/Foo::set_Bar(string)
IL_00c7: nop
IL_00c8: ret
} // end of method Program::Main
Como podemos ver, las dos primeras inicializaciones del
Customer
se han convertido en IL idénticas (IL_0001 a IL_000c).
Después de eso, se vuelve más interesante: desde IL_000d hasta IL_0041, vemos que se crea un nuevo objeto y se le asigna a la variable temporal invisible
<>g__initLocal0
y solo después de que se hace eso, el valor resultante se asigna a
c3
.
Así es como el compilador de C # implementa el inicializador de objetos.
La diferencia para establecer las propiedades públicas "manualmente" después de crear una instancia del objeto es obvia cuando observa cómo se inicializa
c4
de IL_0042 a IL_006a: no hay una variable temporal.
IL_0070 hasta el final son equivalentes a los ejemplos anteriores, excepto que usan un constructor no predeterminado en combinación con inicializadores de objetos. Como es de esperar, simplemente se compila de la misma manera que antes, pero con el constructor especificado.
En pocas palabras: el resultado, desde el punto de vista de un desarrollador de C #, es básicamente el mismo. Los inicializadores de objetos son simples trucos de compilación y azúcar sintácticos que pueden ayudar a que el código sea más fácil de leer. Sin embargo, FWIW, la IL resultante no es idéntica a la inicialización manual de las propiedades. Aún así, el costo de la variable temporal invisible que emite el compilador debería ser insignificante en casi todos los programas de C #.