Warum lässt C # schreibgeschützte lokale Variablen nicht zu?

115

Eine freundschaftliche Debatte mit einem Kollegen darüber führen. Wir haben einige Gedanken darüber, aber fragen uns, was die SO-Menge darüber denkt?

Brian Genisio
quelle
4
@ColonelPanic C und C ++ haben konstante lokale Variablen, die Sie mit einem zur Laufzeit berechneten Wert initialisieren können.
Crashworks
1
JavaScript 2015 (ES6) hat den Typ const. ZB {const myList = [1,2,3]; }. Es ist eine sehr gute Programmierpraxis, dieses Konstrukt zu verwenden. Weitere Informationen: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox
1
Für Interessenten gibt es einen UserVoice-Vorschlag für diese Funktion . Es hat derzeit nur 87 Stimmen. Wenn Sie also lokale schreibgeschützte Variablen sehen möchten, stoßen Sie bitte darauf!
Ian Kemp
1
Es ist nicht nur ein Sprachproblem. Es ist ein Problem einer Mehrheit in der C # -Gemeinschaft, einschließlich der am höchsten bewerteten C # -Gurus, dass sie sich nicht um die Richtigkeit der Konstanten und alles, was damit zusammenhängt, kümmern. Widerstand ist zwecklos.
Patrick Fromberg
1
Update 2017 : Bitte stimmen Sie für die Feature-Anfrage ab, die im C # Language Design Repo diskutiert wird! github.com/dotnet/csharplang/issues/188
Colonel Panic

Antworten:

15

Ein Grund ist, dass es keine CLR-Unterstützung für einen schreibgeschützten lokalen gibt. Readonly wird zunächst in den CLR / CLI-Opcode übersetzt. Dieses Flag kann nur auf Felder angewendet werden und hat keine Bedeutung für ein lokales. In der Tat wird die Anwendung auf einen lokalen Code wahrscheinlich zu nicht überprüfbarem Code führen.

Dies bedeutet nicht, dass C # dies nicht tun konnte. Aber es würde dem gleichen Sprachkonstrukt zwei verschiedene Bedeutungen geben. Die Version für Einheimische hätte keine CLR-äquivalente Zuordnung.

JaredPar
quelle
57
Es hat eigentlich nichts mit der CLI-Unterstützung für die Funktion zu tun, da lokale Variablen in keiner Weise anderen Assemblys ausgesetzt sind. Das readonlySchlüsselwort für Felder muss von der CLI unterstützt werden, da seine Auswirkung für andere Assemblys sichtbar ist . Es würde lediglich bedeuten, dass die Variable zur Kompilierungszeit nur eine Zuordnung in der Methode hat.
Sam Harwell
16
Ich denke, Sie haben gerade die Frage dahingehend verschoben, warum die CLR dies nicht unterstützt, anstatt das Rationale dahinter zu liefern. Es erlaubt konstante Einheimische, so dass es vernünftig wäre, auch schreibgeschützte Einheimische zu erwarten.
Chad Schouggins
9
Ein Beispiel hierfür sind Variablen, die in einer using-Anweisung definiert sind. Sie sind lokal ... und schreibgeschützt (versuchen Sie, sie zuzuweisen, C # fügt einen Fehler hinzu).
Softlion
7
-1 In C ++ gibt es keine Unterstützung für Maschinencode const(was in C ++ eher C # readonlyals C # ähnelt const, obwohl es beide Rollen spielen kann). C ++ unterstützt jedoch constlokale automatische Variablen. Daher ist das Fehlen einer CLR-Unterstützung für ein C # readonlyfür eine lokale Variable irrelevant.
Prost und hth. - Alf
5
1. Dies kann leicht eine Compiler-Funktion sein, wie in C ++. Die CLR-Unterstützung ist völlig irrelevant. Die Maschinenmontage unterstützt es auch nicht, na und? 2. (es würde) wahrscheinlich nicht überprüfbaren Code produzieren - ich verstehe nicht wie, aber vielleicht irre ich mich. 3. es würde dem gleichen Sprachkonstrukt zwei verschiedene Bedeutungen geben - ich bezweifle, dass irgendjemand dies als Problem ansehen würde, da usingund outgenau das tun und die Welt nicht zusammenbricht.
Lou
66

Ich denke, es ist ein schlechtes Urteil von Seiten der C # -Architekten. Der schreibgeschützte Modifikator für lokale Variablen hilft bei der Aufrechterhaltung der Programmkorrektheit (genau wie bei Asserts) und kann dem Compiler möglicherweise dabei helfen, den Code zu optimieren (zumindest bei anderen Sprachen). Die Tatsache, dass es derzeit in C # nicht zulässig ist, ist ein weiteres Argument dafür, dass einige der "Merkmale" von C # lediglich eine Durchsetzung des persönlichen Codierungsstils seiner Ersteller sind.

andriej
quelle
11
Ich stimme dem Teil "Speichern Sie den Programmierer vor sich selbst" zu, aber um dem Compiler bei der Optimierung des Codes zu helfen, bin ich der Meinung, dass der Compiler sehr gut herausfinden kann, ob sich eine Variable im Verlauf der Methode ändert oder nicht, und entsprechend optimiert in jedem Fall. Das Platzieren eines "schreibgeschützten" Flags vor etwas, das der Optimierer zu diesem Zweck sowieso erkennt, ist nicht wirklich vorteilhaft, aber möglicherweise irreführend.
Cornelius
1
@Cornelius Ich stimme zu, dass es Meinungen gibt, dass der Compiler in einigen Fällen ein Datenflussdiagramm verwendet, um Optimierungsmöglichkeiten unabhängig von Schlüsselwörtern / Modifikatoren herauszufinden. Aber die Rettung der Programmierer von sich aus schriftlich falsch und sonst unnötig unoptimized Code kann , dass die Optimierung Gelegenheit für Compiler öffnen.
Shuva
Führen moderne Compiler ohnehin keine statische Einzelzuweisung durch? In diesem Fall ist es in Bezug auf Optimierungen umstritten (aber wenn ein Compiler SSA unterstützt, bedeutet dies, dass es auch trivial ist, einmal zugewiesene lokale Variablen zu implementieren).
Dai
33

Bei der Beantwortung von Jareds Antwort müsste es sich wahrscheinlich nur um eine Funktion zur Kompilierungszeit handeln. Der Compiler würde Ihnen verbieten, nach der ersten Deklaration (die eine Zuweisung enthalten müsste) in die Variable zu schreiben.

Kann ich darin Wert sehen? Potenziell - aber nicht viel, um ehrlich zu sein. Wenn Sie nicht leicht erkennen können, ob eine Variable an anderer Stelle in der Methode zugewiesen wird oder nicht, ist Ihre Methode zu lang.

Für was es wert ist , hat Java diese Funktion (mit dem finalModifikator) , und ich habe sehr selten es als in den Fällen verwendet , um andere zu sehen , wo es hat verwendet werden , um die Variable zu ermöglichen , von einer anonymen inneren Klasse erfaßt werden - und wo es ist verwendet, gibt es mir eher einen Eindruck von Unordnung als nützliche Informationen.

Jon Skeet
quelle
75
Es gibt einen Unterschied, ob Sie sehen, ob eine Variable in Ihrer Methode durch Sicht und durch den Compiler geändert wird oder nicht . Ich sehe keine Einwände dagegen, eine Methode zu schreiben, meine Absicht zu erklären, eine Variable nicht zu ändern, und mich vom Compiler benachrichtigen zu lassen, wenn ich dies versehentlich tue (möglicherweise mit einem Tippfehler einen Monat später)!
A. Rex
50
Andererseits sind in F # alle Variablen standardmäßig schreibgeschützt, und Sie müssen das Schlüsselwort 'veränderlich' verwenden, wenn Sie sie ändern möchten. Da F # eine .NET-Sprache ist, kann ich mir vorstellen, dass sie die von Ihnen beschriebene Überprüfung der Kompilierungszeit durchführt.
Joel Mueller
2
@ A.Rex: Die Frage ist wirklich, ob der Vorteil, den Compiler dazu zu bringen, diese Überprüfung durchzuführen, den zusätzlichen "Flaum" wert ist, wenn der Code gelesen wird und sich nicht wirklich darum kümmert.
Jon Skeet
3
FWIW, Scala unterscheidet lokale readonly/ finalWerte von Variablen mit ihren valund varSchlüsselwörtern. Im Scala-Code werden lokale vals sehr häufig verwendet (und werden tatsächlich lokalen vars vorgezogen ). Ich vermute, dass die Hauptgründe dafür, dass der finalModifikator in Java nicht häufiger verwendet wird, a) Unordnung und b) Faulheit sind.
Aaron Novstrup
4
Für lokale Variablen, die nicht in Abschlüssen verwendet werden, readonlywäre dies nicht übermäßig wichtig. Andererseits readonlywürde der Compiler für lokale Variablen, die in Abschlüssen verwendet werden, in vielen Fällen effizienteren Code generieren lassen. Wenn die Ausführung in einen Block eintritt, der einen Abschluss enthält, muss der Compiler derzeit ein neues Heap-Objekt für die geschlossenen Variablen erstellen, selbst wenn kein Code ausgeführt wird, der den Abschluss verwenden würde . Wenn eine Variable schreibgeschützt wäre, könnte Code außerhalb des Abschlusses eine normale Variable verwenden. Nur wenn ein Delegat für die Schließung erstellt wird ...
Supercat
30

Ein Vorschlag, der nur Einheimische und Parameter enthält, wurde vom C # 7-Designteam kurz besprochen. Aus C # Design Meeting Notes für den 21. Januar 2015 :

Parameter und Einheimische können von Lambdas erfasst und gleichzeitig abgerufen werden, aber es gibt keine Möglichkeit, sie vor Problemen des gemeinsamen gegenseitigen Zustands zu schützen: Sie können nicht schreibgeschützt werden.

Im Allgemeinen sollen die meisten Parameter und viele Einheimische niemals zugewiesen werden, nachdem sie ihren Anfangswert erhalten haben. Wenn sie nur zugelassen werden, würde dies ihre Absicht klar zum Ausdruck bringen.

Ein Problem ist, dass diese Funktion ein "attraktives Ärgernis" sein könnte. Während das "Richtige" fast immer darin besteht, Parameter und Einheimische schreibgeschützt zu machen, würde dies den Code erheblich überladen.

Eine Idee, um dies teilweise zu mildern, besteht darin, zuzulassen, dass die Kombination readonly var für eine lokale Variable auf val oder etwas Kurzes wie dieses kontrahiert wird. Allgemeiner könnten wir versuchen, einfach an ein kürzeres Schlüsselwort als das etablierte Readonly zu denken, um die Readonly-Ness auszudrücken.

Die Diskussion wird im C # Language Design Repo fortgesetzt. Stimmen Sie ab, um Ihre Unterstützung zu zeigen. https://github.com/dotnet/csharplang/issues/188

Oberst Panik
quelle
könnte auch schreibgeschützt zum Standard machen (über eine Option oder ein Schlüsselwort oder was auch immer). Die meisten Variablen und Parameter sollten schreibgeschützt sein und nur wenige beschreibbar sein. und das Minimieren der beschreibbaren Anzahl ist im Allgemeinen eine gute Sache.
Dave Cousineau
12

Es ist ein Versehen für C # -Sprachendesigner. F # hat das Schlüsselwort val und basiert auf CLR. Es gibt keinen Grund, warum C # nicht dieselbe Sprachfunktion haben kann.

Derek Liang
quelle
7

Ich war dieser Mitarbeiter und es war nicht freundlich! (Ich mache nur Spaß)

Ich würde die Funktion nicht eliminieren, da es besser ist, kurze Methoden zu schreiben. Es ist ein bisschen so, als würde man sagen, man sollte keine Threads verwenden, weil sie hart sind. Gib mir das Messer und lass mich dafür verantwortlich sein, dass ich mich nicht schneide.

Persönlich wollte ich ein anderes Schlüsselwort vom Typ "var" wie "inv" (invarient) oder "rvar", um Unordnung zu vermeiden. Ich habe in letzter Zeit F # studiert und finde die unveränderliche Sache ansprechend.

Ich wusste nie, dass Java das hat.

Mike
quelle
5

Ich möchte lokale schreibgeschützte Variablen auf die gleiche Weise wie lokale const- Variablen. Aber es hat weniger Priorität als andere Themen.
Vielleicht ist seine Priorität der gleiche Grund für C # -Designer, diese Funktion (noch!) Nicht zu implementieren. Es sollte jedoch einfach (und abwärtskompatibel) sein, lokale schreibgeschützte Variablen in zukünftigen Versionen zu unterstützen.

Brgerner
quelle
2

Schreibgeschützt bedeutet, dass die Instanzvariable nur im Konstruktor festgelegt werden kann. Wenn eine Variable lokal deklariert wird, hat sie keine Instanz (sie befindet sich nur im Gültigkeitsbereich) und kann vom Konstruktor nicht berührt werden.

Jason Punyon
quelle
6
Das ist die aktuelle Bedeutung von "schreibgeschützt" in C #, aber das ist nicht die Frage. 'schreibgeschützt' hat eine englische Bedeutung, die eine intuitive Anwendung auf eine lokale Variable zu haben scheint: Sie können nicht darauf schreiben (nachdem sie initialisiert wurde). Das scheint der Bedeutung sehr ähnlich zu sein, wenn es auf Instanzvariablen angewendet wird. Warum können wir es also (wie zur Rechtfertigung, glaube ich) nicht auf lokale Variablen anwenden?
Spike0xff
0

Ich weiß, das beantwortet nicht das Warum Ihrer Frage. Wie auch immer, diejenigen, die diese Frage lesen, werden den folgenden Code dennoch zu schätzen wissen.

Wenn Sie sich beim Überschreiben einer lokalen Variablen, die nur einmal festgelegt werden soll, wirklich darum kümmern, sich selbst in den Fuß zu schießen, und Sie sie nicht zu einer global zugänglichen Variablen machen möchten, können Sie so etwas tun.

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

Anwendungsbeispiel:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

Vielleicht nicht so wenig Code wie, rvar rInt = 5aber es funktioniert.

Mike de Klerk
quelle
Das hilft hier nicht weiter. Dieses Problem mit der Variablen 'var' ist: {var five = 5 five = 6; Assert.That (fünf == 5)}
Murray
0

Sie können schreibgeschützte lokale Variablen in C # deklarieren, wenn Sie den interaktiven C # -Compiler verwenden csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Sie können auch schreibgeschützte lokale Variablen im Skriptformat deklarieren .csx.

Oberst Panik
quelle
5
Laut Fehlermeldung messagehandelt es sich hier nicht um eine Variable, sondern um ein Feld. Dies ist kein Nitpicking, da die Unterscheidung auch in interaktivem C # eindeutig vorhanden int x; Console.WriteLine(x)ist : ist legal interaktives C # (weil xes ein Feld ist und implizit initialisiert wird), aber void foo() { int x; Console.WriteLine(x); }nicht (weil xes eine Variable ist und verwendet wird, bevor es zugewiesen wird). Außerdem Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTypewird sich herausstellen, dass xes sich wirklich um ein Feld und nicht um eine lokale Variable handelt.
Jeroen Mostert
0

c # hat bereits eine schreibgeschützte Variable, wenn auch in einer etwas anderen Syntax:

Betrachten Sie die folgenden Zeilen:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

Vergleichen mit:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

Zugegeben, die erste Lösung könnte möglicherweise weniger zu schreibender Code sein. Das zweite Snippet macht das Readonly jedoch explizit, wenn auf die Variable verwiesen wird.

Wolfgang Grinfeld
quelle
Das ist nicht "schreibgeschützt". Dies ist eine lokale Funktion, die eine erfasste Variable zurückgibt. So viel unnötiger Overhead und hässliche Syntax, die die zugrunde liegende Variable aus Sicht des Codezugriffs nicht einmal schreibgeschützt macht. Außerdem gilt "veränderlich" normalerweise für Objekte, nicht für lokale Variablen. Beachten Sie : readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
user2864740
-2

verwenden const Schlüsselwort, um eine schreibgeschützte Variable zu erstellen.

Referenz: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}
Derek Liang
quelle
1
Wir interessieren uns für den JavaScript-Stil, constbei dem die Variable möglicherweise nur während der Initialisierung zugewiesen wird, nicht für den csharp-Stil, constbei dem nur Ausdrücke zur Kompilierungszeit verwendet werden dürfen. ZB können Sie nicht tun, const object c = new object();aber ein readonlyEinheimischer würde Ihnen erlauben, dies zu tun.
Binki
-5

Ich denke, das liegt daran, dass eine Funktion mit einer schreibgeschützten Variablen möglicherweise nie aufgerufen wird und wahrscheinlich etwas außerhalb des Gültigkeitsbereichs liegt, und wann müssten Sie dies tun?

Scottm
quelle