In C # (und vielen anderen Sprachen) ist es absolut legitim, auf private Felder anderer Instanzen desselben Typs zuzugreifen. Beispielsweise:
public class Foo
{
private bool aBool;
public void DoBar(Foo anotherFoo)
{
if (anotherFoo.aBool) ...
}
}
Wie in der C # -Spezifikation (Abschnitte 3.5.1, 3.5.2) angegeben, erfolgt der Zugriff auf private Felder auf einem Typ und nicht auf einer Instanz. Ich habe dies mit einem Kollegen besprochen und wir versuchen, einen Grund zu finden, warum dies so funktioniert (anstatt den Zugriff auf dieselbe Instanz einzuschränken).
Das beste Argument, das wir finden könnten, sind Gleichheitsprüfungen, bei denen die Klasse möglicherweise auf private Felder zugreifen möchte, um die Gleichheit mit einer anderen Instanz zu bestimmen. Gibt es noch andere Gründe? Oder ein goldener Grund, der absolut bedeutet, dass es so funktionieren muss oder etwas völlig unmöglich wäre?
Antworten:
Ich denke, ein Grund, warum dies so funktioniert, ist, dass Zugriffsmodifikatoren zur Kompilierungszeit funktionieren . Daher ist es nicht einfach festzustellen, ob ein bestimmtes Objekt auch das aktuelle Objekt ist oder nicht. Betrachten Sie beispielsweise diesen Code:
Kann der Compiler unbedingt herausfinden, dass dies
other
tatsächlich der Fall istthis
? Nicht in allen Fällen. Man könnte argumentieren, dass dies dann einfach nicht kompiliert werden sollte, aber das bedeutet, dass wir einen Codepfad haben, auf den ein privates Instanzmitglied der richtigen Instanz nicht zugreifen kann, was meiner Meinung nach noch schlimmer ist.Nur erfordern Typ-Ebene und nicht auf Objektebene Sichtbarkeit gewährleistet , dass das Problem handhabbar ist, sowie eine Situation zu machen , die wie es scheint , sollte funktionieren tatsächlich Arbeit.
EDIT : Danilel Hilgarths Argument, dass diese Argumentation rückwärts ist, hat sich bewährt. Sprachdesigner können die gewünschte Sprache erstellen, und Compiler-Autoren müssen sich daran anpassen. Abgesehen davon haben Sprachdesigner einen gewissen Anreiz, Compiler-Autoren die Arbeit zu erleichtern. (In diesem Fall ist es jedoch leicht zu argumentieren, dass auf private Mitglieder nur über
this
(entweder implizit oder explizit) zugegriffen werden kann .)Ich glaube jedoch, dass dies das Problem verwirrender macht, als es sein muss. Die meisten Benutzer (ich selbst eingeschlossen) würden es unnötig einschränken, wenn der obige Code nicht funktioniert: Immerhin sind das meine Daten, auf die ich zugreifen möchte ! Warum sollte ich durchmachen müssen
this
?Kurz gesagt, ich glaube, ich habe den Fall, dass es für den Compiler "schwierig" ist, übertrieben. Was ich wirklich vermitteln wollte, ist, dass die obige Situation so aussieht, als ob die Designer gerne arbeiten würden.
quelle
this.bar = 2
aber nichtother.bar = 2
, daother
dies eine andere Instanz sein könnte.Da der Zweck der in C # und ähnlichen Sprachen * verwendeten Kapselung darin besteht, die gegenseitige Abhängigkeit verschiedener Codeteile (Klassen in C # und Java) und nicht verschiedener Objekte im Speicher zu verringern.
Wenn Sie beispielsweise Code in eine Klasse schreiben, die einige Felder in einer anderen Klasse verwendet, sind diese Klassen sehr eng miteinander verbunden. Wenn Sie jedoch mit Code arbeiten, in dem Sie zwei Objekte derselben Klasse haben, besteht keine zusätzliche Abhängigkeit. Eine Klasse hängt immer von sich selbst ab.
All diese Theorie über die Kapselung schlägt jedoch fehl, sobald jemand Eigenschaften erstellt (oder Paare in Java abruft / setzt) und alle Felder direkt verfügbar macht, wodurch Klassen so gekoppelt werden, als würden sie sowieso auf Felder zugreifen.
* Zur Klärung der Einkapselungsarten siehe Abels ausgezeichnete Antwort.
quelle
get/set
Methoden bereitgestellt werden. Accessor-Methoden behalten weiterhin eine Abstraktion bei (der Benutzer muss nicht wissen, dass sich dahinter ein Feld befindet oder welche Felder sich möglicherweise hinter der Eigenschaft befinden). Wenn wir beispielsweise eineComplex
Klasse schreiben , können wir Eigenschaften zum Abrufen / Festlegen der Polarkoordinaten und ein weiteres Abrufen / Festlegen für kartesische Koordinaten verfügbar machen. Welche die Klasse darunter verwendet, ist dem Benutzer unbekannt (es könnte sogar etwas ganz anderes sein).private
. Sie können meine Antwort auf meine Meinung zu Dingen überprüfen, wenn Sie möchten;).Zu diesem interessanten Thread wurden bereits einige Antworten hinzugefügt, aber ich habe den wahren Grund nicht gefunden, warum dieses Verhalten so ist, wie es ist. Lassen Sie es mich versuchen:
Damals
Irgendwo zwischen Smalltalk in den 80ern und Java Mitte der 90er Jahre reifte das Konzept der Objektorientierung. Das Verstecken von Informationen, das ursprünglich nicht als Konzept gedacht war, das nur OO zur Verfügung stand (erstmals 1978 erwähnt), wurde in Smalltalk eingeführt, da alle Daten (Felder) einer Klasse privat und alle Methoden öffentlich sind. Während der vielen neuen Entwicklungen von OO in den 90er Jahren versuchte Bertrand Meyer, einen Großteil der OO-Konzepte in seinem wegweisenden Buch Object Oriented Software Construction (OOSC) zu formalisieren, das seitdem als (fast) endgültige Referenz zu OO-Konzepten und Sprachdesign gilt .
Bei privater Sichtbarkeit
Laut Meyer sollte eine Methode einer definierten Gruppe von Klassen zur Verfügung gestellt werden (Seite 192-193). Dies führt offensichtlich zu einer sehr hohen Granularität des Versteckens von Informationen. Die folgende Funktion steht Klasse A und Klasse B sowie allen ihren Nachkommen zur Verfügung:
Im Fall von
private
sagt er Folgendes: Ohne einen Typ explizit als für seine eigene Klasse sichtbar zu deklarieren, können Sie in einem qualifizierten Aufruf nicht auf diese Funktion (Methode / Feld) zugreifen. Dh wennx
es sich um eine Variable handelt,x.doSomething()
ist dies nicht zulässig. Unqualifizierter Zugriff ist natürlich innerhalb der Klasse selbst erlaubt.Mit anderen Worten: Um den Zugriff einer Instanz derselben Klasse zuzulassen, müssen Sie den Methodenzugriff dieser Klasse explizit zulassen. Dies wird manchmal als instanzprivat gegenüber klassenprivat bezeichnet.
Instanz-privat in Programmiersprachen
Ich kenne mindestens zwei Sprachen, die derzeit verwendet werden und die das Verstecken von instanzprivaten Informationen im Gegensatz zum Verstecken von klassenprivaten Informationen verwenden. Eine davon ist Eiffel, eine von Meyer entworfene Sprache, die OO auf die Spitze treibt. Das andere ist Ruby, eine heutzutage weit verbreitete Sprache. In Ruby
private
bedeutet: "privat für diese Instanz" .Auswahlmöglichkeiten für die Sprachgestaltung
Es wurde vorgeschlagen, dass es für den Compiler schwierig sein würde, eine Instanz privat zuzulassen. Ich denke nicht, da es relativ einfach ist, qualifizierte Aufrufe von Methoden nur zuzulassen oder zu verbieten. Wenn für eine private Methode
doSomething()
zulässig ist undx.doSomething()
nicht, hat ein Sprachdesigner die Nur-Instanz-Zugänglichkeit für private Methoden und Felder effektiv definiert.Aus technischer Sicht gibt es keinen Grund, sich für die eine oder andere Art zu entscheiden (insbesondere wenn man bedenkt, dass Eiffel.NET dies mit IL tun kann, gibt es auch bei Mehrfachvererbung keinen inhärenten Grund, diese Funktion nicht bereitzustellen).
Natürlich ist es Geschmackssache, und wie andere bereits erwähnt haben, sind einige Methoden möglicherweise schwieriger zu schreiben, ohne dass private Methoden und Felder auf Klassenebene sichtbar sind.
Warum C # nur die Klassenkapselung und nicht die Instanzkapselung zulässt
Wenn Sie sich Internet-Threads zur Instanzkapselung ansehen (ein Begriff, der manchmal verwendet wird, um die Tatsache zu bezeichnen, dass eine Sprache die Zugriffsmodifikatoren auf Instanzebene im Gegensatz zur Klassenebene definiert), wird das Konzept häufig verpönt. Wenn man jedoch bedenkt, dass einige moderne Sprachen die Instanzkapselung verwenden, zumindest für den Modifikator für den privaten Zugriff, denken Sie, dass dies in der modernen Programmierwelt von Nutzen sein kann und ist.
Allerdings hat C # C ++ und Java für sein Sprachdesign zugegebenermaßen am härtesten untersucht. Während Eiffel und Modula-3 ebenfalls im Bild waren, glaube ich, dass sie angesichts der vielen fehlenden Funktionen von Eiffel (Mehrfachvererbung) dieselbe Route wie Java und C ++ gewählt haben, wenn es um den Modifikator für den privaten Zugriff ging.
Wenn Sie wirklich wissen möchten, warum Sie versuchen sollten, Eric Lippert, Krzysztof Cwalina, Anders Hejlsberg oder andere Personen zu erreichen, die am Standard von C # gearbeitet haben. Leider konnte ich in der kommentierten Programmiersprache The C # keinen endgültigen Hinweis finden .
quelle
Foo
effektiv eine Schnittstelle und eine implementierende Klasse darstellte. Da COM kein Konzept für eine Klassenobjektreferenz hatte - nur eine Schnittstellenreferenz -, konnte eine Klasse auf keinen Fall eine Referenz auf etwas enthalten, das garantiert eine andere Instanz dieser Klasse war. Dieses schnittstellenbasierte Design machte einige Dinge umständlich, aber es bedeutete, dass man eine Klasse entwerfen konnte, die durch eine andere ersetzt werden konnte, ohne dass sie dieselben Interna teilen musste.Dies ist nur meine Meinung, aber pragmatisch gesehen denke ich, wenn ein Programmierer Zugriff auf die Quelle einer Klasse hat, können Sie ihnen vernünftigerweise den Zugriff auf die privaten Mitglieder der Klasseninstanz anvertrauen. Warum die rechte Hand eines Programmierers binden, wenn Sie ihm in der linken bereits die Schlüssel zum Königreich gegeben haben?
quelle
Der Grund ist in der Tat Gleichheitsprüfung, Vergleich, Klonen, Überladen von Operatoren ... Es wäre sehr schwierig, Operator + beispielsweise für komplexe Zahlen zu implementieren.
quelle
readonly
und dies gilt auch für die deklarierende Instanz. Offensichtlich behaupten Sie, dass es eine bestimmte Compiler-Regel geben sollte, um dies zu verbieten, aber jede solche Regel ist eine Funktion, die das C # -Team spezifizieren, dokumentieren, lokalisieren, testen, warten und unterstützen muss. Wenn es keinen zwingenden Nutzen gibt, warum sollten sie es dann tun?Was würde zunächst mit privaten statischen Mitgliedern geschehen? Kann nur mit statischen Methoden auf sie zugegriffen werden? Das würden Sie sicher nicht wollen, denn dann könnten Sie nicht auf Ihre
const
s zugreifen .Betrachten Sie bei Ihrer expliziten Frage den Fall a
StringBuilder
, der als verknüpfte Liste von Instanzen von sich selbst implementiert ist:Wenn Sie nicht auf die privaten Mitglieder anderer Instanzen Ihrer eigenen Klasse zugreifen können, müssen Sie Folgendes implementieren
ToString
:Dies wird funktionieren, aber es ist O (n ^ 2) - nicht sehr effizient. In der Tat macht das wahrscheinlich den ganzen Zweck zunichte, überhaupt eine
StringBuilder
Klasse zu haben. Wenn Sie auf die privaten Mitglieder anderer Instanzen Ihrer eigenen Klasse zugreifen können , können Sie dies implementieren,ToString
indem Sie eine Zeichenfolge mit der richtigen Länge erstellen und dann eine unsichere Kopie jedes Blocks an der entsprechenden Stelle in der Zeichenfolge erstellen:Diese Implementierung ist O (n), was sie sehr schnell macht und nur möglich ist, wenn Sie Zugriff auf private Mitglieder anderer Instanzen Ihrer Klasse haben .
quelle
internal
macht es weniger privat! Am Ende würden Sie die Interna Ihrer Klasse allem in ihrer Versammlung aussetzen.Dies ist in vielen Sprachen (zum Beispiel C ++) absolut legitim. Die Zugriffsmodifikatoren stammen aus dem Kapselungsprinzip in OOP. Die Idee ist, den Zugriff nach außen zu beschränken , in diesem Fall nach außen sind andere Klassen. Jede verschachtelte Klasse in C # kann beispielsweise auch auf die privaten Mitglieder ihrer Eltern zugreifen.
Dies ist zwar eine Design-Wahl für einen Sprachdesigner. Die Einschränkung dieses Zugriffs kann einige sehr häufige Szenarien extrem komplizieren, ohne viel zur Isolation von Entitäten beizutragen.
Es gibt eine ähnliche Diskussion hier
quelle
Ich glaube nicht, dass es einen Grund gibt, warum wir keine weitere Datenschutzstufe hinzufügen können, bei der Daten für jede Instanz privat sind. In der Tat könnte dies sogar ein gutes Gefühl der Vollständigkeit der Sprache vermitteln.
Aber in der Praxis bezweifle ich, dass es wirklich so nützlich wäre. Wie Sie bereits betont haben, ist unsere übliche Privatität nützlich für Dinge wie Gleichheitsprüfungen sowie für die meisten anderen Operationen, an denen mehrere Instanzen eines Typs beteiligt sind. Ich mag jedoch auch Ihren Standpunkt zur Aufrechterhaltung der Datenabstraktion, da dies ein wichtiger Punkt in OOP ist.
Ich denke, die Möglichkeit, den Zugriff so einzuschränken, könnte eine nette Funktion sein, die man zu OOP hinzufügen kann. Ist es wirklich so nützlich? Ich würde nein sagen, da eine Klasse in der Lage sein sollte, ihrem eigenen Code zu vertrauen. Da diese Klasse die einzigen Dinge sind, die auf private Mitglieder zugreifen können, gibt es keinen wirklichen Grund, eine Datenabstraktion zu benötigen, wenn es sich um eine Instanz einer anderen Klasse handelt.
Natürlich können Sie Ihren Code immer so schreiben, als ob privat auf Instanzen angewendet würde. Verwenden Sie die üblichen
get/set
Methoden, um auf die Daten zuzugreifen / diese zu ändern. Dies würde den Code wahrscheinlich übersichtlicher machen, wenn die Klasse internen Änderungen unterliegen könnte.quelle
Tolle Antworten oben gegeben. Ich würde hinzufügen, dass ein Teil dieses Problems die Tatsache ist, dass das Instanziieren einer Klasse in sich selbst überhaupt erst erlaubt ist. Da in einer rekursiven Logik "for" -Schleife beispielsweise diese Art von Trick verwendet wird, solange Sie über Logik verfügen, um die Rekursion zu beenden. Das Instanziieren oder Übergeben derselben Klasse in sich selbst, ohne solche Schleifen zu erzeugen, birgt jedoch logischerweise eigene Gefahren, obwohl dies ein weit verbreitetes Programmierparadigma ist. Beispielsweise kann eine C # -Klasse eine Kopie von sich selbst in ihrem Standardkonstruktor instanziieren, dies verstößt jedoch nicht gegen Regeln und erzeugt keine Kausalschleifen. Warum?
Übrigens ... das gleiche Problem gilt auch für "geschützte" Mitglieder. :((
Ich habe dieses Programmierparadigma nie vollständig akzeptiert, da es immer noch eine ganze Reihe von Problemen und Risiken mit sich bringt, die die meisten Programmierer erst dann vollständig erfassen, wenn Probleme wie dieses auftauchen und die Menschen verwirren und dem ganzen Grund für die Mitgliedschaft privater Mitglieder trotzen.
Dieser "seltsame und verrückte" Aspekt von C # ist ein weiterer Grund, warum gute Programmierung nichts mit Erfahrung und Können zu tun hat, sondern nur die Tricks und Fallen zu kennen ... wie an einem Auto zu arbeiten. Es ist das Argument, dass Regeln gebrochen werden sollten, was ein sehr schlechtes Modell für jede Computersprache ist.
quelle
Es scheint mir, dass Daten, die für andere Instanzen desselben Typs privat wären, nicht mehr unbedingt vom selben Typ wären. Es scheint sich nicht wie andere Instanzen zu verhalten oder zu verhalten. Das Verhalten kann leicht basierend auf diesen privaten internen Daten geändert werden. Das würde meiner Meinung nach nur Verwirrung stiften.
Ich persönlich denke, dass das Schreiben von Klassen, die von einer Basisklasse abgeleitet sind, ähnliche Funktionen bietet, die Sie mit "private Daten pro Instanz haben" beschreiben. Stattdessen haben Sie nur eine neue Klassendefinition pro 'eindeutigem' Typ.
quelle