Warum sind const-Parameter in C # nicht zulässig?

102

Es sieht besonders für C ++ - Entwickler seltsam aus. In C ++ haben wir einen Parameter als markiert const, um sicherzustellen, dass sein Status in der Methode nicht geändert wird. Es gibt auch andere C ++ - spezifische Gründe, z. B. das Übergeben const ref, um ref zu übergeben und sicherzustellen, dass der Status nicht geändert wird. Aber warum können wir in C # nicht als Methodenparameter const markieren?

Warum kann ich meine Methode nicht wie folgt deklarieren?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....
Inkognito
quelle
Es war immer nur eine Sicherheitsfunktion in C ++ und nie wirklich ein wesentlicher Bestandteil der Sprache, wie ich sie sah. C # -Entwickler glaubten offensichtlich nicht, dass dies einen großen Mehrwert darstellte.
Noldorin
3
Eine größere Diskussion bei dieser Frage: "Const-Korrektheit in c #": stackoverflow.com/questions/114149/const-correctness-in-c
Tenpn
Es gibt für Werttypen [out / ref], aber keine Referenztypen. Das ist eine interessante Frage.
Kenny
4
Wie könnte diese Frage sowohl die C # - als auch die sprachunabhängigen Tags haben?
CJ7
1
Unveränderliche Daten und Funktionen ohne Nebenwirkungen sind leichter zu begründen. Sie könnten F # fsharpforfunandprofit.com/posts/is-your-language-unreasonable
Colonel Panic

Antworten:

59

Zusätzlich zu den anderen guten Antworten werde ich noch einen weiteren Grund hinzufügen, warum C-artige Konstanz nicht in C # eingefügt werden sollte. Du sagtest:

Wir markieren den Parameter als const, um sicherzustellen, dass sein Status in der Methode nicht geändert wird.

Wenn const das tatsächlich tun würde, wäre das großartig. Const macht das nicht. Die Konstante ist eine Lüge!

Const bietet keine Garantie dafür, dass ich sie tatsächlich verwenden kann. Angenommen, Sie haben eine Methode, die eine konstante Sache akzeptiert. Es gibt zwei Codes Autoren: die Person , die das Schreiben Anrufer und die Person , die das Schreiben Angerufenen . Der Autor des Angerufenen hat die Methode zu einer Konstante gemacht. Was können die beiden Autoren davon ausgehen, dass das Objekt unveränderlich ist?

Nichts. Dem Angerufenen steht es frei, die Konstante wegzuwerfen und das Objekt zu mutieren, sodass der Aufrufer keine Garantie dafür hat, dass der Aufruf einer Methode, die eine Konstante verwendet, diese tatsächlich nicht mutiert. Ebenso kann der Angerufene nicht davon ausgehen, dass sich der Inhalt des Objekts während der Aktion des Angerufenen nicht ändert. Der Angerufene könnte eine Mutationsmethode für einen nicht konstanten Alias des const-Objekts aufrufen , und jetzt hat sich das sogenannte const-Objekt geändert .

C-style const bietet keine Garantie dafür, dass sich das Objekt nicht ändert, und ist daher fehlerhaft. Jetzt hat C bereits ein schwaches Typsystem, in dem Sie eine Umwandlung eines Double in ein int neu interpretieren können, wenn Sie es wirklich wollen. Daher sollte es keine Überraschung sein, dass es auch in Bezug auf const ein schwaches Typsystem hat. Aber C # wurde entwickelt, um ein gutes Typsystem zu haben, ein Typsystem, bei dem, wenn Sie sagen "diese Variable enthält eine Zeichenfolge", die Variable tatsächlich einen Verweis auf eine Zeichenfolge (oder null) enthält. Wir wollen absolut keinen C-artigen "const" -Modifikator in das Typsystem einfügen, weil wir nicht wollen, dass das Typsystem eine Lüge ist . Wir möchten, dass das Typsystem stark ist, damit Sie Ihren Code richtig beurteilen können.

Const in C ist eine Richtlinie ; es bedeutet im Grunde "Sie können mir vertrauen, dass ich nicht versuche, dieses Ding zu mutieren". Das sollte nicht im Typensystem sein ; Das Zeug im Typsystem sollte eine Tatsache über das Objekt sein , über die Sie nachdenken können, und keine Richtlinie für seine Verwendung.

Versteh mich jetzt nicht falsch; Nur weil const in C tief gebrochen ist, heißt das nicht, dass das gesamte Konzept nutzlos ist. Was ich gerne sehen würde, ist eine tatsächlich korrekte und nützliche Form der "const" -Anmerkung in C #, eine Annotation, die sowohl Menschen als auch Compiler verwenden könnten, um ihnen das Verständnis des Codes zu erleichtern, und die die Laufzeit verwenden könnte, um Dinge wie automatische Paralellisierung und andere erweiterte Optimierungen.

Stellen Sie sich zum Beispiel vor, Sie könnten ein Kästchen um einen Codestück "zeichnen" und sagen "Ich garantiere, dass dieser Codestück keine Mutationen zu einem Feld dieser Klasse ausführt", auf eine Weise, die vom Compiler überprüft werden könnte. Oder zeichnen Sie eine Box mit der Aufschrift "Diese reine Methode mutiert den internen Zustand des Objekts, jedoch nicht in einer Weise, die außerhalb der Box beobachtet werden kann". Ein solches Objekt könnte nicht sicher automatisch mit mehreren Threads versehen werden, aber es könnte automatisch gespeichert werden . Es gibt alle Arten von interessanten Anmerkungen, die wir auf Code setzen könnten, um umfassende Optimierungen und ein tieferes Verständnis zu ermöglichen. Wir können es viel besser machen als die schwache C-artige Const-Annotation.

Ich betone jedoch, dass dies nur Spekulation ist . Wir haben keine festen Pläne, diese Art von Feature in eine hypothetische zukünftige Version von C # zu integrieren, wenn es überhaupt eine gibt, die wir nicht auf die eine oder andere Weise angekündigt haben. Es ist etwas, das ich gerne sehen würde und das die kommende Betonung des Multi-Core-Computing möglicherweise erfordert, aber nichts davon sollte in irgendeiner Weise als Vorhersage oder Garantie für ein bestimmtes Merkmal oder eine zukünftige Richtung für C # ausgelegt werden.

Wenn Sie lediglich eine Anmerkung zur lokalen Variablen wünschen, bei der es sich um einen Parameter handelt, der besagt, dass sich der Wert dieses Parameters während der gesamten Methode nicht ändert, ist dies natürlich problemlos möglich. Wir könnten "schreibgeschützte" Lokale und Parameter unterstützen, die einmal initialisiert werden, und einen Fehler bei der Kompilierung, der in der Methode geändert werden muss. Die von der Anweisung "using" deklarierte Variable ist bereits eine solche lokale Variable. Wir könnten allen Einheimischen und Parametern eine optionale Anmerkung hinzufügen, damit sie sich wie "using" -Variablen verhalten. Es war noch nie eine Funktion mit sehr hoher Priorität, daher wurde es nie implementiert.

Eric Lippert
quelle
34
Entschuldigung, aber ich finde dieses Argument sehr schwach. Die Tatsache, dass ein Entwickler eine Lüge sagen kann, kann in keiner Sprache verhindert werden. void foo (const A & a), das das Objekt a ändert, unterscheidet sich nicht von int countApples (Basket b), die die Anzahl der Birnen zurückgeben. Es ist nur ein Fehler, ein Fehler, der in jeder Sprache kompiliert wird.
Alessandro Teruzzi
55
"Der Angerufene kann die Konstante wegwerfen ..." uhhhhh ... (° _ °) Dies ist ein ziemlich flaches Argument, das Sie hier vorbringen. Dem Angerufenen steht es auch frei, zufällige Speicherorte mit zu schreiben 0xDEADBEEF. Aber beide wären sehr schlecht programmiert und würden von jedem modernen Compiler früh und ergreifend erfasst. Verwechseln Sie in Zukunft beim Schreiben von Antworten bitte nicht das, was technisch möglich ist, indem Sie die Hintertüren einer Sprache mit dem verwenden, was im tatsächlichen Code möglich ist. Solche verzerrten Informationen verwirren weniger erfahrene Leser und beeinträchtigen die Qualität der Informationen zu SO.
Slipp D. Thompson
8
"Const in C ist eine Richtlinie;" Das Zitieren einer Quelle für einige der Dinge, die Sie sagen, würde Ihrem Fall hier wirklich helfen. Nach meiner Ausbildung und Branchenerfahrung lautet die zitierte Aussage Hogwash - const in C ist ebenso obligatorisch wie das Typsystem selbst - beide haben Hintertüren, um sie bei Bedarf zu betrügen (wie wirklich alles in C), werden aber erwartet vom Buch in 99,99% des Codes verwendet werden.
Slipp D. Thompson
29
Diese Antwort ist der Grund, warum ich manchmal C # -Design hasse. Die Sprachdesigner sind sich absolut sicher, dass sie viel schlauer sind als andere Entwickler und versuchen, sie übermäßig vor Fehlern zu schützen. Es ist offensichtlich, dass das Schlüsselwort const nur in der Kompilierungszeit funktioniert, da wir in C ++ direkten Zugriff auf den Speicher haben und alles tun können, was wir wollen, und verschiedene Möglichkeiten haben, um die const-Deklaration zu umgehen. Aber niemand tut dies in normalen Situationen. Übrigens bieten 'privat' und 'geschützt' in C # auch keine Garantie, da wir über Reflektion auf diese Methoden oder Felder zugreifen können.
BlackOverlord
13
@BlackOverlord: (1) Der Wunsch, eine Sprache zu entwerfen, deren Eigenschaften Entwickler natürlich eher zum Erfolg als zum Scheitern führen, wird durch den Wunsch motiviert , nützliche Tools zu erstellen , und nicht durch die Überzeugung, dass die Designer schlauer sind als ihre Kunden, wie Sie vermuten. (2) Reflexion ist kein Teil der C # -Sprache; es ist Teil der Laufzeit; Der Zugriff auf Reflection wird durch das Sicherheitssystem der Laufzeit eingeschränkt. Code mit geringem Vertrauen kann keine private Reflexion durchführen. Die von Zugriffsmodifikatoren bereitgestellten Garantien gelten für Code mit geringer Vertrauenswürdigkeit . Code mit hohem Vertrauen kann alles.
Eric Lippert
24

Einer der Gründe, warum es in C # keine konstante Korrektheit gibt, ist, dass es auf Laufzeitebene nicht vorhanden ist. Denken Sie daran, dass C # 1.0 keine Funktion hatte, es sei denn, es war Teil der Laufzeit.

Und mehrere Gründe, warum die CLR keine Vorstellung von konstanter Korrektheit hat, sind zum Beispiel:

  1. Dies verkompliziert die Laufzeit. Außerdem hatte die JVM es auch nicht und die CLR begann im Grunde genommen als Projekt, um eine JVM-ähnliche Laufzeit zu erstellen, keine C ++ - ähnliche Laufzeit.
  2. Wenn auf Laufzeitebene eine konstante Korrektheit vorliegt, sollte die BCL eine konstante Korrektheit aufweisen, andernfalls ist die Funktion für .NET Framework ziemlich sinnlos.
  3. Wenn die BCL jedoch Konstantenkorrektheit erfordert, sollte jede Sprache über der CLR Konstantenkorrektheit unterstützen (VB, JavaScript, Python, Ruby, F # usw.). Dies wird nicht passieren.

Const-Korrektheit ist so ziemlich eine Sprachfunktion, die nur in C ++ vorhanden ist. Es läuft also ziemlich genau auf die gleiche Argumentation hinaus, warum die CLR keine geprüften Ausnahmen erfordert (was eine reine Java-Sprachfunktion ist).

Ich glaube auch nicht, dass Sie eine so grundlegende Systemfunktion in einer verwalteten Umgebung einführen können, ohne die Abwärtskompatibilität zu beeinträchtigen. Verlassen Sie sich also nicht darauf, dass die Konstanz-Korrektheit jemals in die C # -Welt eintritt.

Ruben
quelle
Sind Sie sicher, dass JVM es nicht hat? Wie ich weiß, hat es es. Sie können public static void TestMethod (final int val) in Java ausprobieren.
Inkognito
2
Finale ist nicht wirklich const. Sie können weiterhin Felder im Referenz-IIRC ändern, nur nicht den Wert / die Referenz selbst. (Was sowieso als Wert übergeben wird, ist also eher ein Compiler-Trick, um zu verhindern, dass Sie den Parameterwert ändern.)
Ruben
1
Ihre 3. Kugel ist nur teilweise wahr. Konstante Parameter für BCL-Aufrufe zu haben, wäre keine bahnbrechende Änderung, da sie lediglich den Vertrag genauer beschreiben. "Diese Methode ändert den Status des Objekts nicht" im Gegensatz zu "Diese Methode erfordert, dass Sie einen konstanten Wert übergeben", der brechen würde
Rune FS
2
Es wird innerhalb der Methode erzwungen , sodass es keine bahnbrechende Änderung wäre, es in die BCL einzuführen.
Rune FS
1
Selbst wenn die Laufzeit dies nicht erzwingen könnte, wäre es hilfreich, ein Attribut zu haben, das angibt, ob eine Funktion in einen refParameter schreiben darf / soll (insbesondere im Fall von Strukturmethoden / -eigenschaften this). Wenn eine Methode ausdrücklich angibt, dass sie nicht in einen refParameter schreibt, sollte der Compiler die Übergabe schreibgeschützter Werte zulassen. Wenn umgekehrt eine Methode angibt, dass sie schreiben wird this, sollte der Compiler nicht zulassen, dass eine solche Methode für schreibgeschützte Werte aufgerufen wird.
Supercat
12

Ich glaube, es gibt zwei Gründe, warum C # nicht konstant ist.

Das erste ist die Verständlichkeit . Nur wenige C ++ - Programmierer verstehen die Richtigkeit von Konstanten. Das einfache Beispiel von const int argist niedlich, aber ich habe auch gesehen char * const * const arg- einen konstanten Zeiger auf konstante Zeiger auf nicht konstante Zeichen. Die Konstanzkorrektur von Zeigern auf Funktionen ist eine völlig neue Ebene der Verschleierung.

Das zweite ist, weil Klassenargumente Referenzen sind, die als Wert übergeben werden. Dies bedeutet, dass bereits zwei Ebenen der Konstanz ohne eine offensichtlich klare Syntax zu bewältigen sind . Ein ähnlicher Stolperpunkt sind Sammlungen (und Sammlungen von Sammlungen usw.).

Konstantenkorrektheit ist ein wichtiger Bestandteil des C ++ - Typsystems. Es könnte theoretisch zu C # als etwas hinzugefügt werden, das nur zur Kompilierungszeit überprüft wird (es muss nicht zur CLR hinzugefügt werden und würde die BCL nur beeinflussen, wenn der Begriff der const-Member-Methoden enthalten wäre). .

Ich halte dies jedoch für unwahrscheinlich: Der zweite Grund (Syntax) wäre ziemlich schwer zu lösen, was den ersten Grund (Verständlichkeit) noch problematischer machen würde.

Stephen Cleary
quelle
1
Du hast Recht , dass CLR nicht wirklich Sorgen machen müssen darüber, wie man verwenden kann modoptund modreqauf Metadatenebene , dass Informationen zu speichern (wie C + / CLI bereits tut - siehe System.Runtime.CompilerServices.IsConst); und dies könnte trivial auch auf const-Methoden ausgedehnt werden.
Pavel
Es lohnt sich, muss aber typsicher erfolgen. Die von Ihnen erwähnte Deep / Shallow-Konstante öffnet eine ganze Dose Würmer.
user1496062
In einem C ++, in dem Referenzen tatsächlich manipuliert werden, kann ich Ihr Argument über zwei Arten von const sehen. In C # (wo Sie durch Hintertüren gehen müssen, um "Zeiger" zu verwenden) scheint mir jedoch klar genug zu sein, dass es eine genau definierte Bedeutung für Referenztypen (dh Klassen) und eine genau definierte, wenn auch unterschiedliche Bedeutung für Wert gibt Typen (dh Primitive, Strukturen)
Assimilater
2
Programmierer, die Konstanz nicht verstehen können, sollten nicht in C ++ programmieren.
Steve White
5

constbedeutet "Kompilierungszeitkonstante" in C #, nicht "schreibgeschützt, aber möglicherweise durch anderen Code veränderbar" wie in C ++. Ein grobes Analogon von C ++ constin C # ist readonly, aber dieses gilt nur für Felder. Abgesehen davon gibt es in C # überhaupt keinen C ++ - ähnlichen Begriff der Konstantenkorrektheit.

Die Begründung ist schwer zu sagen, da es viele mögliche Gründe gibt, aber ich vermute, dass ich die Sprache in erster Linie einfach halten möchte und unsichere Vorteile bei der Implementierung dieser zweiten.

Pavel Minaev
quelle
Sie denken also, MS betrachtet "Rauschen" als konstante Parameter in Methoden.
Inkognito
1
Ich bin mir nicht sicher, was "Lärm" ist - ich würde es lieber "hohes Kosten-Nutzen-Verhältnis" nennen. Aber natürlich brauchen Sie jemanden vom C # -Team, der es Ihnen mit Sicherheit sagt.
Pavel Minaev
Wie ich es verstehe, ist Ihre Argumentation, dass Konstanz aus C # herausgelassen wurde, um es einfach zu halten. Denk darüber nach.
Steve White
2

Dies ist seit C # 7.2 (Dezember 2017 mit Visual Studio 2017 15.5) möglich.

Nur für Strukturen und Grundtypen ! , nicht für Klassenmitglieder.

Sie müssen verwenden in, um das Argument als Eingabe als Referenz zu senden. Sehen:

Siehe: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

Für Ihr Beispiel:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
isgoed
quelle