Warum ist String.Empty keine Konstante?

188

Warum ist String.Empty in .Net schreibgeschützt anstelle einer Konstante? Ich frage mich nur, ob jemand weiß, was die Gründe für diese Entscheidung waren.

Travis
quelle
5
Diese Frage kann dieses
Problem
Ja, +1 für die Antwort von Eric Lippert, danke!
Travis
Besonders wenn man bedenkt, dass Decimal.Zero const ist (aus der Sicht des Benutzers ist das ...)
Hamish Grubijan

Antworten:

147

Der Grund, static readonlyder anstelle von verwendet wird, constliegt in der Verwendung mit nicht verwaltetem Code, wie von Microsoft hier in der Common Language Infrastructure 2.0-Version von Shared Source angegeben . Die zu betrachtende Datei ist sscli20\clr\src\bcl\system\string.cs.

Die leere Konstante enthält den leeren Zeichenfolgenwert. Wir müssen den String-Konstruktor aufrufen, damit der Compiler dies nicht als Literal markiert.

Wenn Sie dies als Literal markieren, wird es nicht als Feld angezeigt, auf das wir von native aus zugreifen können.

Ich habe diese Informationen aus diesem praktischen Artikel bei CodeProject gefunden .

Jeff Yates
quelle
Ich würde mich sehr freuen, wenn Sie diesen Kommentar erklären können (weil Jon Skeet nicht ...), siehe hier: stackoverflow.com/questions/8462697/…
gdoron unterstützt Monica
2
@gdoron: Meine Vermutung (und es ist eine Vermutung) ist dies. Wenn ein Wert als Literal (eine Konstante) definiert ist, wird sein Wert an den Stellen eingefügt, an denen auf ihn verwiesen wird. Wenn er nicht als Literal definiert ist, wird auf die Quelle des Werts verwiesen und der tatsächliche Wert zur Laufzeit abgerufen. Ich vermute, dass letzteres sicherstellen kann, dass zur Laufzeit ein ordnungsgemäßes Marshalling der Zeichenfolge zwischen native und .NET erfolgt. Wenn es sich um ein Literal handelt, müsste der native Compiler den Literalwert möglicherweise irgendwie in seinen nativen Code ziehen, was wahrscheinlich nicht der Fall ist möglich. Dies ist jedoch alles eine Vermutung von meiner Seite.
Jeff Yates
7
Dies bedeutet, dass "" anstelle von string.Empty für Standardparameterwerte in Methoden verwendet werden muss. Welches ist etwas nervig.
nicodemus13
17
"" kann wie ein Fehler aussehen, während string.Empty zeigt absichtliche Absicht
Christopher Stevenson
3
@ JeffYates Ich würde hinzufügen, dass die Tatsache, dass es nicht konsistent ist, bereits ärgerlich ist. Die Leute würden den Rest des Codes sehen und sich fragen, warum er hier "anstelle von String.Empty" verwendet. Ich denke ernsthaft darüber String.Emptynach, allein aus diesem Grund nicht mehr zu verwenden.
Julealgon
24

Ich denke, hier gibt es viel Verwirrung und schlechte Reaktionen.

Zuallererst sind constFelder staticMitglieder ( keine Instanzmitglieder ).

Überprüfen Sie Abschnitt 10.4 Konstanten der C # -Sprachspezifikation.

Obwohl Konstanten als statische Elemente betrachtet werden, erfordert oder erlaubt eine Konstantendeklaration keinen statischen Modifikator.

Wenn public const Mitglieder statisch sind, kann man nicht berücksichtigen, dass eine Konstante ein neues Objekt erstellt.

Vor diesem Hintergrund machen die folgenden Codezeilen genau dasselbe in Bezug auf die Erstellung eines neuen Objekts.

public static readonly string Empty = "";
public const string Empty = "";

Hier ist ein Hinweis von Microsoft, der den Unterschied zwischen den beiden erklärt:

Das schreibgeschützte Schlüsselwort unterscheidet sich vom const-Schlüsselwort. Ein const-Feld kann nur bei der Deklaration des Feldes initialisiert werden. Ein schreibgeschütztes Feld kann entweder in der Deklaration oder in einem Konstruktor initialisiert werden. Daher können schreibgeschützte Felder je nach verwendetem Konstruktor unterschiedliche Werte haben. Während ein const-Feld eine Konstante zur Kompilierungszeit ist, kann das schreibgeschützte Feld auch für Laufzeitkonstanten verwendet werden, ...

Daher finde ich, dass die einzig plausible Antwort hier die von Jeff Yates ist.

bruno conde
quelle
+1 für die freundlichen Worte und Klarstellungen bezüglich der C # -Spezifikation für const und static readonly.
Jeff Yates
17
Wenn ich das noch einmal lese, bin ich anderer Meinung const stringund static readonly stringmache das Gleiche. Konstantenwerte werden im verknüpften Code ersetzt, während auf statische schreibgeschützte Werte verwiesen wird. Wenn Sie eine constin Bibliothek A haben, die von Bibliothek B verwendet wird, ersetzt Bibliothek B alle Verweise auf diese constVariable durch ihren Literalwert. Wenn diese Variable static readonlystattdessen wäre, würde sie zur Laufzeit referenziert und ihr Wert bestimmt.
Jeff Yates
3
Jeffs Punkt ist wichtig, wenn auf Bibliotheken verwiesen wird. Wenn Sie A neu kompilieren und neu verteilen, ohne B neu zu kompilieren , verwendet B weiterhin die alten Werte.
Mark Sowul
4
String.Empty read only instead of a constant?

Wenn Sie eine Zeichenfolge konstant machen , wird der Compiler durch die tatsächliche Zeichenfolge ersetzt überall dort, wo Sie ihn aufrufen , und Sie füllen Ihren Code überall mit derselben Zeichenfolge. Wenn der Code ausgeführt wird, muss diese Zeichenfolge auch immer wieder aus dem anderen Speicher gelesen werden Daten.

Wenn Sie Ihre Zeichenfolge nur an einer Stelle lesen, wie es die ist String.Empty an einer Stelle schreibgeschützt lassen, behält das Programm dieselbe Zeichenfolge nur an einer Stelle bei und liest sie oder verweist darauf - wobei die Daten im Speicher minimal bleiben.

Auch wenn Sie eine DLL mit String.Empty als const kompilieren und aus irgendeinem Grund die String.Empty-Änderung, funktioniert die kompilierte DLL nicht mehr gleich, da die cost Code eine Kopie der Zeichenfolge enthält bei jedem Anruf.

Siehe diesen Code zum Beispiel:

public class OneName
{
    const string cConst = "constant string";
    static string cStatic = "static string";
    readonly string cReadOnly = "read only string";

    protected void Fun()
    {
        string cAddThemAll ;

        cAddThemAll = cConst;
        cAddThemAll = cStatic ;
        cAddThemAll = cReadOnly;    
    }
}

wird vom Compiler kommen als:

public class OneName
{
    // note that the const exist also here !
    private const string cConst = "constant string";
    private readonly string cReadOnly;
    private static string cStatic;

    static OneName()
    {
        cStatic = "static string";
    }

    public OneName()
    {
        this.cReadOnly = "read only string";
    }

    protected void Fun()
    {
        string cAddThemAll ;

        // look here, will replace the const string everywhere is finds it.
        cAddThemAll = "constant string";
        cAddThemAll = cStatic;
        // but the read only will only get it from "one place".
        cAddThemAll = this.cReadOnly;

    }
}

und der Versammlungsaufruf

        cAddThemAll = cConst;
0000003e  mov         eax,dword ptr ds:[09379C0Ch] 
00000044  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cStatic ;
00000047  mov         eax,dword ptr ds:[094E8C44h] 
0000004c  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cReadOnly;
0000004f  mov         eax,dword ptr [ebp-3Ch] 
00000052  mov         eax,dword ptr [eax+0000017Ch] 
00000058  mov         dword ptr [ebp-44h],eax 

Bearbeiten: Tippfehler korrigiert

Aristos
quelle
Das bedeutet also, dass die const-Zeichenfolge immer mit der Klasse instanziiert werden muss, die diese const enthält? Das scheint viel besser zu sein, als statisch schreibgeschützt zu verwenden.
Der Berserker
@theberserker gut ist besser, aber Sie haben alle Optionen zu verwenden.
Aristos
> dann funktioniert die kompilierte DLL nicht mehr gleich, da die Kosten den Insider-Code dazu bringen, bei jedem Aufruf tatsächlich eine Kopie der Zeichenfolge zu behalten. @ Aristos Das ist nicht ganz richtig. Sobald der Code kompiliert ist, wird auf die "Kopie" der Zeichenfolge im TEXT-Block der ausführbaren Datei verwiesen, und der gesamte Code verweist nur auf denselben Speicherblock. Was Sie in Ihrem zweiten Schritt zitiert haben, ist einfach ein Zwischenschritt.
Peter Dolkens
@ user1533523 danke für den Hinweis - ich werde einen Test machen, wenn ich etwas Zeit finde, dies zu überprüfen
Aristos
Wie haben Sie diesen Assembler-Code erhalten? C # wird nicht zur Assembly kompiliert!
Jv110
0

Diese Antwort existiert für historische Zwecke.

Ursprünglich:

weil String es eine Klasse ist und daher keine Konstante sein kann.

Erweiterte Diskussion:

Bei der Überprüfung dieser Antwort wurden viele nützliche Dialoge herausgearbeitet, und anstatt sie zu löschen, wird dieser Inhalt direkt reproduziert:

In .NET sind (anders als in Java) Zeichenfolge und Zeichenfolge genau gleich. Und ja, Sie können String-Literal-Konstanten in .NET - DrJokepu 3. Februar 09 um 16:57 haben

Wollen Sie damit sagen, dass eine Klasse keine Konstanten haben kann? - StingyJack 3. Februar 09 um 16:58 Uhr

Ja, Objekte müssen schreibgeschützt verwendet werden. Nur Strukturen können Konstanten ausführen. Ich denke, wenn Sie stringanstelle von verwendenString Compilers verwenden, ändert sich die Konstante in eine für Sie schreibgeschützte. Alles damit zu tun, C-Programmierer bei Laune zu halten. - Garry Shutler 3. Februar 09 um 16:59 Uhr

tvanfosson hat es nur etwas ausführlicher erklärt. "X kann keine Konstante sein, weil das enthaltende Y eine Klasse ist" war nur ein bisschen zu kontextfrei;) - Leonidas 3. Februar 09 um 17:01 Uhr

string.Empty ist eine statische Eigenschaft, die eine Instanz der String-Klasse zurückgibt, nämlich die leere Zeichenfolge, nicht die Zeichenfolgenklasse selbst. - Tvanfosson 3. Februar 09 um 17:01 Uhr

Leer ist eine schreibgeschützte Instanz (keine Eigenschaft) der String-Klasse. - Senfo 3. Februar 09 um 17:02 Uhr

Der Kopf tut weh. Ich denke immer noch, dass ich Recht habe, aber jetzt bin ich mir weniger sicher. Forschung heute Abend erforderlich! - Garry Shutler 3. Februar 09 um 17:07 Uhr

Die leere Zeichenfolge ist eine Instanz der Zeichenfolgenklasse. Leer ist ein statisches Feld (keine Eigenschaft, ich stehe korrigiert) in der String-Klasse. Grundsätzlich der Unterschied zwischen einem Zeiger und dem, worauf er zeigt. Wenn es nicht schreibgeschützt wäre, könnten wir ändern, auf welche Instanz sich das Feld Leer bezieht. - Tvanfosson 3. Februar 09 um 17:07 Uhr

Garry, du musst keine Nachforschungen anstellen. Denk darüber nach. String ist eine Klasse. Leer ist eine Instanz eines Strings. - Senfo 3. Februar 09 um 17:12 Uhr

Es gibt etwas, das ich nicht ganz verstehe: Wie um alles in der Welt kann der statische Konstruktor der String-Klasse eine Instanz der String-Klasse erstellen? Ist das nicht eine Art "Huhn oder Ei" -Szenario? - DrJokepu 3. Februar 09 um 17:12 5

Diese Antwort wäre für fast jede andere Klasse außer System.String richtig. .NET bietet eine Menge Performance-Spezialgehäuse für Zeichenfolgen. Eine davon ist, dass Sie Zeichenfolgenkonstanten haben können. Probieren Sie es einfach aus. In diesem Fall hat Jeff Yates die richtige Antwort. - Joel Mueller 3. Februar 09 um 19:25 Uhr

Wie in §7.18 beschrieben, ist ein konstanter Ausdruck ein Ausdruck, der zur Kompilierungszeit vollständig ausgewertet werden kann. Da die einzige Möglichkeit, einen Nicht-Null-Wert eines anderen Referenztyps als einer Zeichenfolge zu erstellen, darin besteht, den neuen Operator anzuwenden, und der neue Operator in einem Konstantenausdruck nicht zulässig ist, ist dies der einzig mögliche Wert für Konstanten von Referenztypen andere als Zeichenfolge ist null. Die beiden vorherigen Kommentare wurden direkt aus der C # -Sprachenspezifikation übernommen und wiederholen, was Joel Mueller erwähnte. - Senfo 4. Februar 09 um 15:05 5

Garry Shutler
quelle
Bitte stimmen Sie die richtige Antwort ab. Wenn Sie zur Definition wechseln, werden Sie feststellen, dass sie sich in der String-Klasse befindet und eine Instanz von String ist. Die Tatsache, dass es als Kleinbuchstaben angezeigt wird, ist Compilermagie.
Garry Shutler
Ich habe Sie nicht herabgestimmt, aber in .NET (im Gegensatz zu Java) sind String und String genau gleich. Und ja, Sie können String-Literal-Konstanten in .NET
Tamas Czinege
10
Diese Antwort wäre für fast jede andere Klasse außer System.String richtig. .NET bietet eine Menge Performance-Spezialgehäuse für Zeichenfolgen. Eine davon ist, dass Sie Zeichenfolgenkonstanten haben können. Probieren Sie es einfach aus. In diesem Fall hat Jeff Yates die richtige Antwort.
Joel Mueller
7
Ich habe diese Antwort fast gelöscht, da eine viel bessere kam, aber die Diskussion in diesen Kommentaren ist es wert, beibehalten zu werden.
Garry Shutler
1
@Garry, du hast Glück, dass ich deinen letzten Kommentar gelesen habe, sonst würde ich auch abstimmen. Ein String hat in .NET eine Besonderheit: Wenn es sich um eine Ref-Klasse handelt, kann es sich um eine Konstante handeln.
Shimmy Weitzhandler