it-swarm.com.de

Warum wird der parameterlose Standardkonstruktor beim Erstellen eines Parameters mit Parametern entfernt?

Wenn Sie in C #, C++ und Java einen Konstruktor erstellen, der Parameter verwendet, wird der standardmäßige parameterlose Parameter weggelassen. Ich habe diese Tatsache immer akzeptiert, aber jetzt frage ich mich warum.

Was ist der Grund für dieses Verhalten? Ist es nur eine "Sicherheitsmaßnahme/Vermutung", die besagt "Wenn Sie einen eigenen Konstruktor erstellt haben, möchten Sie wahrscheinlich wollen, dass dieser nicht implizit in der Nähe hängt"? Oder hat er einen technischen Grund macht es dem Compiler unmöglich, einen hinzuzufügen, nachdem Sie einen Konstruktor selbst erstellt haben?

156
olagjo

Es gibt keinen Grund, warum der Compiler den Konstruktor nicht hinzufügen konnte, wenn Sie Ihren eigenen hinzugefügt haben - der Compiler könnte so ziemlich alles tun, was er will! Man muss jedoch schauen, was am sinnvollsten ist:

  • Wenn ich den any-Konstruktor nicht für eine nicht statische Klasse definiert habe, möchte ich höchstwahrscheinlich diese Klasse instanziieren können. Um dies zu ermöglichen, fügt der Compiler must einen parameterlosen Konstruktor hinzu, der keine Wirkung hat, als Instantiierung. Das bedeutet, dass ich keinen leeren Konstruktor in meinen Code einfügen muss, damit er funktioniert.
  • Wenn ich einen eigenen Konstruktor definiert habe, insbesondere einen mit Parametern, dann habe ich wahrscheinlich eine eigene Logik, die beim Erstellen der Klasse ausgeführt werden muss. Wenn der Compiler in diesem Fall einen leeren, parameterlosen Konstruktor erstellen würde, würde es jemandem erlauben, die Logik, die ich geschrieben habe, zu überspringen, was dazu führen könnte, dass mein Code in vielerlei Hinsicht bricht. Wenn ich in diesem Fall einen leeren Standardkonstruktor haben möchte, muss ich dies explizit sagen.

In jedem Fall können Sie also sehen, dass das Verhalten der aktuellen Compiler am sinnvollsten ist, um die wahrscheinliche Absicht des Codes zu erhalten.

218
Dan Puzey

Es gibt sicherlich keinen technischen Grund, warum die Spracheso gestaltet werden muss.

Es gibt vier etwas realistische Optionen, die ich sehen kann:

  1. Überhaupt keine Standardkonstruktoren
  2. Das aktuelle Szenario
  3. ImmerStandardkonstruktor angeben, aber explizit unterdrücken lassen
  4. Geben Sie immer einen Standardkonstruktorohnean, damit er unterdrückt werden kann

Option 1 ist insofern etwas attraktiv, als je mehr ich codiere, desto seltener möchte ichreallyeinen parameterlosen Konstruktor. Eines Tages sollte ich zählen, wie oft ichtatsächlichmit einem Standardkonstruktor ende ...

Option 2: Mir geht es gut.

Option 3 widerspricht sowohl Java als auch C # für den Rest der Sprache. Es gibt nie etwas, das Sie explizit "entfernen", es sei denn, Sie möchten Dinge explizit privater machen, als dies in Java standardmäßig der Fall ist.

Option 4 ist schrecklich - Sieabsolutmöchten in der Lage sein, die Konstruktion mit bestimmten Parametern zu erzwingen. Was würde new FileStream() überhaupt bedeuten?

Im Grunde genommen ist, wennSie die Annahme akzeptieren, dass die Angabe eines Standardkonstruktors überhaupt Sinn macht. Ich glaube, es ist sehr sinnvoll, ihn zu unterdrücken, sobald Sie Ihren eigenen angeben Konstrukteur.

69
Jon Skeet

Bearbeiten. Das, was ich in meiner ersten Antwort sage, ist zwar gültig, aber das ist der wahre Grund:

Am Anfang gab es C. C ist nicht objektorientiert (Sie können eine Annäherung OO = nehmen, aber es hilft Ihnen nicht oder erzwingt nichts).

Dann gab es C With Classes, das später in C++ umbenannt wurde. C++ ist objektorientiert und fördert daher die Einkapselung und die Gewährleistung der Invariante eines Objekts. Beim Konstruieren und am Anfang und am Ende einer Methode befindet sich das Objekt in einem gültigen Zustand.

Das Natürlichste dabei ist, zu erzwingen, dass eine Klasse immer über einen Konstruktor verfügen muss, um sicherzustellen, dass sie in einem gültigen Zustand startet. Wenn der Konstruktor dies nicht tun muss, wird der leere Konstruktor dies dokumentieren .

Ein Ziel bei C++ war jedoch, mit C bis zu dem Punkt kompatibel zu sein, dass alle gültigen C-Programme auch gültige C++ - Programme waren (nicht mehr als aktives Ziel, und die Entwicklung von C zu C++ bedeutet, dass es nicht länger gilt ).

Ein Effekt davon war die Verdoppelung der Funktionalität zwischen struct und class. Erstere tun Dinge auf die C-Richtung (alles standardmäßig öffentlich), und letztere tun Dinge auf eine gute OO Weise (alles privat standardmäßig, Entwickler macht aktiv das, was sie wollen, öffentlich).

Eine andere ist, dass, damit eine C struct, die keinen Konstruktor haben kann, weil C keine Konstruktoren hat, in C++ gültig ist, dann eine Bedeutung für die C++ - Sichtweise vorhanden sein muss. Auch wenn ein Konstruktor nicht gegen die OO -Praxis der aktiven Sicherstellung einer Invariante verstoßen würde, meinte C++ dies als einen parameterlosen Standardkonstruktor, der so wirkte, als hätte er einen leeren Körper.

Alle C structs waren jetzt gültige C++ structs (was bedeutete, dass sie mit C++ classes identisch waren, wobei alles - Mitglieder und Vererbung - public) von außen so behandelt wurden, als ob es einen einzigen parameterlosen Konstruktor hätte.

Wenn Sie jedoch einen Konstruktor in eine class oder struct gestellt haben, dann haben Sie die C++/OO-Methode anstelle der C-Methode ausgeführt, und es war kein Standardkonstruktor erforderlich.

Da es als Abkürzung diente, wurde es auch dann verwendet, wenn Kompatibilität sonst nicht möglich war (andere C++ - Funktionen wurden nicht in C verwendet).

Als Java (in vielerlei Hinsicht auf C++ basierend) und später C # (auf verschiedene Weise auf C++ und Java basierend) auf den Markt kam, behielten sie diesen Ansatz bei, da Codierer dies möglicherweise bereits gewohnt waren.

Stroustrup schreibt dazu in seiner The C++ - Programmiersprache und noch mehr mit dem "Warum" der Sprache in The Design and Evolution of C++ .

=== Ursprüngliche Antwort ===

Sagen wir, das ist nicht passiert.

Angenommen, ich möchte keinen parameterlosen Konstruktor, weil ich meine Klasse ohne einen nicht in einen sinnvollen Zustand versetzen kann. Tatsächlich kann dies mit struct in C # passieren (wenn Sie jedoch keine aus Nullen und Nullen bestehenden struct in C # sinnvoll verwenden können, verwenden Sie am besten eine nicht öffentlich sichtbare Optimierung und auf andere Weise haben einen Designfehler bei der Verwendung von struct).

Damit meine Klasse ihre Invarianten schützen kann, brauche ich ein spezielles removeDefaultConstructor-Schlüsselwort. Zumindest muss ich einen privaten parameterlosen Konstruktor erstellen, um sicherzustellen, dass kein aufrufender Code den Standardwert aufruft.

Was die Sprache etwas komplizierter macht. Besser nicht, es zu tun.

Alles in allem ist es am besten, nicht daran zu denken, einen Konstruktor hinzuzufügen, um die Standardeinstellung zu entfernen. Besser ist es, keinen Konstruktor als syntaktischen Zucker für das Hinzufügen eines parameterlosen Konstruktors zu verwenden, der nichts tut.

19
Jon Hanna

Der standardmäßige parameterlose Konstruktor wird hinzugefügt, wenn Sie selbst nichts unternehmen, um die Objekterstellung zu steuern. Nachdem Sie einen einzelnen Konstruktor erstellt haben, um die Kontrolle zu übernehmen, "zieht sich der Compiler zurück" und Sie haben die volle Kontrolle.

Andernfalls müssten Sie den Standardkonstruktor explizit deaktivieren, wenn Sie möchten, dass Objekte nur über einen Konstruktor mit Parametern erstellt werden können.

13
Anders Abel

Ich denke, die Frage sollte umgekehrt sein: Warum müssen Sie keinen Standardkonstruktor deklarieren, wenn Sie noch keine anderen Konstruktoren definiert haben? 

Ein Konstruktor ist für nicht statische Klassen obligatorisch.
Ich denke, wenn Sie keine Konstruktoren definiert haben, ist der generierte Standardkonstruktor nur eine bequeme Funktion des C # -Compilers. Auch Ihre Klasse wäre ohne einen Konstruktor nicht gültig. Also nichts falsch daran, implizit einen Konstruktor zu generieren, der nichts tut. Es sieht auf jeden Fall sauberer aus als leere Konstrukteure. 

Wenn Sie bereits einen Konstruktor definiert haben, ist Ihre Klasse gültig. Warum sollte der Compiler davon ausgehen, dass Sie einen Standardkonstruktor wünschen? Was ist, wenn du keine willst? Implementieren Sie ein Attribut, um den Compiler anzuweisen, diesen Standardkonstruktor nicht zu generieren. Ich denke nicht, dass das eine gute Idee wäre.

3
Botz3000

Dies ist eine praktische Funktion des Compilers. Wenn Sie einen Konstruktor mit Parametern definieren, aber keinen parameterlosen Konstruktor, ist die Möglichkeit, dass Sie keinen parameterlosen Konstruktor zulassen möchten, viel höher.

Dies ist der Fall für viele Objekte, die mit einem leeren Konstruktor nicht initialisiert werden können.

Andernfalls müssten Sie für jede Klasse, die Sie einschränken möchten, einen privaten parameterlosen Konstruktor deklarieren.

Meiner Meinung nach ist es kein guter Stil, einen parameterlosen Konstruktor für eine Klasse zuzulassen, die Parameter benötigt, um zu funktionieren.

3
mw_21

Der Standardkonstruktor kann nur erstellt werden, wenn die Klasse keinen Konstruktor hat. Compiler sind so geschrieben, dass sie nur als Sicherungsmechanismus dienen.

Wenn Sie einen parametrisierten Konstruktor haben, möchten Sie möglicherweise nicht, dass ein Objekt mit dem Standardkonstruktor erstellt wird. Hätte der Compiler einen Standardkonstruktor bereitgestellt, müssten Sie einen No-Arg-Konstruktor schreiben und als privat deklarieren, um zu verhindern, dass Objekte ohne Argumente erstellt werden.

Außerdem besteht eine höhere Wahrscheinlichkeit, dass Sie das Deaktivieren vergessen oder den Standardkonstruktor "privatisieren" und dadurch einen möglichen Funktionsfehler verursachen, der schwer zu fangen ist.

Und jetzt müssen Sie explizit einen No-Arg-Konstruktor definieren, wenn Sie ein Objekt standardmäßig erstellen oder Parameter übergeben möchten. Dies wird streng geprüft und der Compiler beschwert sich anders, so dass hier keine Lücke besteht.

1
ashes

Ich denke, das wird vom Compiler erledigt. Wenn Sie die .net-Assembly in ILDASM öffnen, wird der Standardkonstruktor angezeigt, auch wenn er nicht im Code enthalten ist. Wenn Sie einen parametrisierten Konstruktor definieren, wird der Standardkonstruktor nicht angezeigt. 

Wenn Sie die Klasse definieren (nicht statisch), bietet der Compiler dieses Feature an, dass Sie nur eine Instanz erstellen werden. Und wenn Sie möchten, dass eine bestimmte Operation ausgeführt wird, haben Sie sicherlich einen eigenen Konstruktor. 

1

Prämisse

Dieses Verhalten kann als eine natürliche Erweiterung der Entscheidung betrachtet werden, damit Klassen einen standardmäßigen Konstruktor ohne öffentliche Parameter haben. Basierend auf der gestellten Frage nehmen wir diese Entscheidung als Prämisse und gehen davon aus, dass wir sie in diesem Fall nicht in Frage stellen.

Möglichkeiten zum Entfernen des Standardkonstruktors

Daraus folgt, dass es eine Möglichkeit geben muss, den standardmäßigen öffentlichen parameterlosen Konstruktor zu entfernen. Diese Entfernung kann auf folgende Weise durchgeführt werden:

  1. Deklarieren Sie einen nicht öffentlichen parameterlosen Konstruktor
  2. Entfernen Sie den parameterlosen Konstruktor automatisch, wenn ein Konstruktor mit Parametern deklariert wird
  3. Ein Schlüsselwort/Attribut, das dem Compiler mitgeteilt wird, um den parameterlosen Konstruktor zu entfernen (so unhandlich, dass er leicht ausgeschlossen werden kann)

Auswahl der besten Lösung

Nun fragen wir uns: Wenn es keinen parameterlosen Konstruktor gibt, durch welchen muss er ersetzt werden? und Unter welchen Arten von Szenarien möchten wir den öffentlichen Konstruktor ohne öffentliche Parameter entfernen?

Die Dinge beginnen sich zusammenzureißen. Erstens muss es entweder durch einen Konstruktor mit Parametern oder durch einen nicht öffentlichen Konstruktor ersetzt werden. Zweitens sind die Szenarien, unter denen Sie keinen parameterlosen Konstruktor wünschen, folgende:

  1. Wir möchten nicht, dass die Klasse überhaupt instanziiert wird, oder wir möchten die Sichtbarkeit des Konstruktors steuern: deklarieren einen nicht öffentlichen Konstruktor
  2. Wir möchten, dass Parameter beim Konstruieren angegeben werden: deklarieren einen Konstruktor mit Parametern

Fazit

Da haben wir es - genau die zwei Möglichkeiten, mit denen C #, C++ und Java den öffentlichen Konstruktor ohne Parameter entfernen können.

1
Zaid Masud

Wenn Sie keinen Konstruktor definieren, generiert der Compiler automatisch einen Konstruktor, der keine Argumente akzeptiert. Wenn Sie etwas mehr aus einem Konstruktor heraus haben möchten, übersteuern Sie es. Dies ist KEINE Funktionsüberladung. Der einzige Konstruktor, den der Compiler jetzt sieht, ist also Ihr Konstruktor, der ein Argument benötigt. Um diesem Problem zu begegnen, können Sie einen Standardwert übergeben, wenn der Konstruktor ohne Wert übergeben wird.

0
Vivek Pradhan

Eine Klasse benötigt einen Konstruktor. Es ist zwingende Voraussetzung.

  • Wenn Sie keinen erstellen, wird Ihnen der parameterlose Konstruktor automatisch übergeben.
  • Wenn Sie keinen parameterlosen Konstruktor wünschen, müssen Sie Ihren eigenen erstellen.
  • Wenn Sie sowohl einen parameterlosen Konstruktor als auch einen parameterbasierten Konstruktor benötigen, können Sie diese manuell hinzufügen.

Ich werde Ihre Frage mit einer anderen beantworten. Warum wollen wir immer einen parameterlosen Standardkonstruktor? Es gibt Fälle, in denen dies nicht erwünscht ist, so dass der Entwickler das Steuerelement zum Hinzufügen oder Entfernen nach Bedarf hat.

0
Jaider