2015-01-31 21 views
61

Ich habe gerade festgestellt, dass mit {} anstelle von () die gleichen Ergebnisse beim Erstellen eines Objekts.Funktioniert {} wie() beim Erstellen eines neuen Objekts in C#?

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

Hat {} wirken wie () wenn in C# ein neues Objekt zu erstellen?

+1

Sie * können * Werte in der ctor ['()'] übergeben, wenn Sie eine Überladung bereitstellen, um sie zu akzeptieren und zu verwenden. die geschweiften Klammern sind zum Initialisieren – Plutonix

+0

Keine Antwort, weil Sie bereits diese, nur einen Kommentar: '{}' bietet eine Möglichkeit, Werte zu assutieren, im Grunde nichts mehr als eine Kurzschrift, um das Objekt zuerst zu instanziieren und dann die Werte nacheinander zuzuweisen . Der Konstrukteur ist jedoch in keiner Weise darauf beschränkt. Es liegt ganz bei Ihnen, welche Argumente Sie übergeben und ob Sie diese Argumente verwenden, um Standardwerte zuzuordnen, Berechnungen durchzuführen oder andere Teile Ihres Codes aufzurufen. –

Antwort

107

Es gibt drei Möglichkeiten, direkt ein neues Objekt in C# zu erstellen:

  • Ein einfacher Konstruktoraufruf mit einer Argumentliste:

    new Foo()  // Empty argument list 
    new Foo(10, 20) // Passing arguments 
    
  • Ein Objektinitialisierer mit einer Argumentliste

    new Foo() { Name = "x" }  // Empty argument list 
    new Foo(10, 20) { Name = "x" } // Two arguments 
    
  • Ein Objektinitialisierer ohne Argument

    Liste
    new Foo { Name = "x" } 
    

Die letzte Form ist exakt äquivalent eine leere Argumentliste angeben. Normalerweise ruft es einen parameterlosen Konstruktor auf, aber könnte einen Konstruktor aufrufen, in dem alle Parameter Standardwerte haben.

nun beide in den Objektinitialisierer Beispiele, die ich gegeben habe, habe ich eine Name Eigenschaft - und Sie könnten auch andere Eigenschaften/Felder gesetzt, oder setzen sogar keine Eigenschaften und Felder. So alle drei von diesen äquivalent sind, effektiv keine Konstruktor Argumente zu übergeben und die Angabe keine Eigenschaften/Felder zu setzen:

new Foo() 
new Foo() {} 
new Foo {} 

Von diesen ist die erste ist die konventionelle.

+10

@NadiaChibrikova: Es klingt, als ob du 'new {}' statt 'new Test {}' benutzt. Es sollte keine anonymen Typen geben. –

+0

Kannst du auch einfach 'new Foo;' mit * weder * Klammern noch Klammern sagen? Wenn nicht, warum nicht? –

+0

Von einem C++ - Hintergrund aus bedeutet das Erstellen eines Objekts ohne Klammern, dass kein Konstruktor aufgerufen wird (für POD-Typen), dh das Objekt wird standardmäßig auf den Müll gesetzt, der sich auf dem Stapel befindet. Das ist wahrscheinlich der Grund, warum es nicht in C# enthalten war - es widerspricht allem, was C# für müllinitialisierte Objekte bedeutet. – Blindy

1

Customer c1= new Customer{} gleiche schaffen - das initializer von Eigenschaften ist. Sie können es als schreiben:

Customer c1 = new Customer{ 
       name="some text", 
       ID="some id" 
       }; 
5

Sie Objektinitialisierer verwenden können Typ-Objekte in einer deklarativen Weise zu initialisieren, ohne explizit einen Konstruktor für die Art aufgerufen wird.

https://msdn.microsoft.com/en-us/library/bb397680.aspx

Auch können Sie den Konstruktor aufrufen weglassen, wenn der Typ Standard contructor hat. So

Customer c1 = new Customer { }; 

ist genau das gleiche wie

Customer c1 = new Customer() { }; 
48

() - ruft den parameterlosen Konstruktor.

{} - soll verwendet werden, um Eigenschaften zuzuweisen.

Die Verwendung von {} ohne () ist eine Verknüpfung und funktioniert so lange, wie ein parameterloser Konstruktor vorhanden ist.

1

In Ihrem spezifischen Szenario, ja, würde es das gleiche Ergebnis produzieren, aber nicht aus einem Grund, den Sie wahrscheinlich denken.

Um das Ergebnis zu verstehen, lassen Sie uns sagen, dass Sie eine Klasse wie dieses:

class Customer 
{ 
    public string name; 
    public string ID {get; set;} 

    public Customer() 
    { 
    } 

    public Customer(string n, string id) 
    { 
     name = n; 
     ID = id; 
    } 
} 

Wenn Sie es wie folgt erstellen:

Customer c = new Customer("john", "someID"); 

Sie den zweiten Konstruktor aufrufen und die Klasse sagen Sie‘ Diese Werte zu übergeben und die Verantwortung dafür zu übernehmen, was es für das Beste hält, liegt am Konstruktor (in diesem Fall würde er diese Werte verwenden, um sie an öffentliche Felder zu übergeben).

Wenn Sie schaffen es wie folgt aus:

Customer c = new Customer { name = "john", ID = "someID" }; 

Sie die öffentlichen Felder selbst einstellen und den leeren Konstruktor verwenden.

In jedem Fall sollten Sie öffentliche Felder vermeiden, weil es nicht sicher ist. Du solltest niemanden von außen so direkt modifizieren lassen. Verwenden Sie stattdessen nur private Felder und verwenden Sie öffentliche Eigenschaften, um den Zugriff von außen zu verwalten!

+0

Ich stimme dem Teil "Vermeidung öffentlicher Felder" zu, aber nicht, weil "es nicht sicher ist". Was ist nicht "sicher" in öffentlichen Bereichen? – Jcl

+0

@Jcl, ich meinte Situationen zum Beispiel, wenn der Ersteller einen Wert offen legen will, aber nicht erwartet hat, dass er von außen modifiziert wird, weil er Fehler erzeugen könnte. Das Unternehmen stellte einen neuen Programmierer ein und er wusste nichts darüber, also änderte er es, was zu zahlreichen Fehlern in der gesamten Anwendung führte. Natürlich verletzt es OOP-Prinzipien usw. Vielleicht war meine Erklärung nicht so klar, aber ich hoffe, dass die OP zumindest die Kernbotschaft verstanden hat. – walther

+0

Das öffentliche Freilegen eines Felds bedeutet, dass Sie nicht wissen können, wann ein Benutzer der Klasse es ändert. Auch wenn Sie in Ihrem Beispiel eine [auto property] (https://msdn.microsoft.com/en-us/library/bb384054.aspx) wie 'ID' verwenden, können Sie die richtigen get/set-Accessoren mit Validierung oder set verwenden verschiedene Zugänglichkeit später. –

3
Customer c1 = new Customer {}; 

Dies ist ein leerer Objektinitialisierer. Gemäß der spec werden Objektinitialisierer den Standardkonstruktor aufrufen, sofern Sie den zu verwendenden Konstruktor nicht angeben. Da keine Initialisierung durchgeführt wird, wird sie genauso kompiliert wie der Standardkonstruktor.

Um die Frage zu beantworten, wenn '{} act [s] wie () beim Erstellen eines neuen Objekts in C#', müssen wir ins Detail gehen. Für alles, was Ihnen wichtig ist, enthalten die resultierenden Objekte dieselben Daten, aber die generierte IL ist nicht identisch.

Das folgende Beispiel

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

dieser IL kompilieren (nur Hauptmethode, Debug ohne Optimierungen)

.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 

Wie wir in identische sehen, haben die ersten beiden Customer Initialisierungen umgewandelt IL (IL_0001 bis IL_000c). Danach wird es interessanter: Von IL_000d bis IL_0041 sehen wir, dass ein neues Objekt erstellt und der unsichtbaren temporären Variablen <>g__initLocal0 zugewiesen wird und erst danach der resultierende Wert c3 zugewiesen wird. So wird der Objektinitialisierer vom C# -Compiler implementiert. Der Unterschied zum manuellen Festlegen der öffentlichen Eigenschaften nach dem Instanziieren des Objekts ist offensichtlich, wenn Sie sich ansehen, wie c4 von IL_0042 zu IL_006a initialisiert wird - es gibt keine temporäre Variable.

IL_0070 bis zum Ende entsprechen den vorherigen Beispielen, außer dass sie einen nicht standardmäßigen Konstruktor in Kombination mit Objektinitialisierern verwenden. Wie Sie vielleicht erwarten, wird es einfach wie zuvor kompiliert, aber mit dem angegebenen Konstruktor.

Lange Rede, kurzer Sinn: Das Ergebnis ist aus der Sicht eines C# Entwicklers im Grunde dasselbe. Objektinitialisierer sind einfache syntaktische Zucker- und Compilertricks, die helfen können, den Code leichter lesbar zu machen. In FWIW ist die resultierende IL jedoch nicht identisch mit der manuellen Initialisierung der Eigenschaften. Dennoch sollten die Kosten der unsichtbaren temporären Variablen, die vom Compiler ausgegeben werden, in fast allen C# -Programmen nicht signifikant sein.

Verwandte Themen