Ich habe einen kleinen Code, der einen Indexwert analysiert, um eine Zelleingabe in Excel zu bestimmen. Ich muss nachdenken ...
Was ist der Unterschied zwischen
xlsSheet.Write("C" + rowIndex.ToString(), null, title);
und
xlsSheet.Write(string.Format("C{0}", rowIndex), null, title);
Ist einer "besser" als der andere? Und warum?
Antworten:
Vor C # 6
Um ehrlich zu sein, denke ich, dass die erste Version einfacher ist - obwohl ich sie vereinfachen würde, um:
Ich vermute , dass andere Antworten können über die Leistung Hit sprechen, aber um ehrlich zu sein es minimal sein würde wenn überhaupt - und diese Verkettung Version entspricht nicht das Format - String analysieren muss.
Formatzeichenfolgen eignen sich hervorragend zum Lokalisieren usw., aber in einem solchen Fall ist die Verkettung einfacher und funktioniert genauso gut.
Mit C # 6
Die String-Interpolation vereinfacht das Lesen vieler Dinge in C # 6. In diesem Fall wird Ihr zweiter Code zu:
Das ist wahrscheinlich die beste Option, IMO.
quelle
xlsSheet.Write($"C{rowIndex}", null, title);
Meine anfängliche Präferenz (aus einem C ++ - Hintergrund) war String.Format. Ich habe dies später aus folgenden Gründen fallen gelassen:
- String-Verkettung erlaubt Nullwerte,String.Format
nicht. Das Schreiben von "s1 + null + s2
" wird nicht unterbrochen, sondern behandelt den Nullwert lediglich als String.Empty. Nun, dies kann von Ihrem spezifischen Szenario abhängen - es gibt Fälle, in denen Sie einen Fehler wünschen, anstatt einen Null-Vornamen stillschweigend zu ignorieren. Aber selbst in dieser Situation bevorzuge ich es persönlich, selbst nach Nullen zu suchen und bestimmte Fehler zu werfen, anstatt der Standard-ArgumentNullException, die ich von String.Format erhalte.Die Idee ist, dass der .NET-Compiler intelligent genug ist, um diesen Code zu konvertieren:
dazu:
Was unter der Haube von String.Concat passiert, ist leicht zu erraten (verwenden Sie Reflector). Die Objekte im Array werden über ToString () in ihre Zeichenfolge konvertiert. Dann wird die Gesamtlänge berechnet und nur eine Zeichenfolge zugewiesen (mit der Gesamtlänge). Schließlich wird jede Zeichenfolge über wstrcpy in einem unsicheren Code in die resultierende Zeichenfolge kopiert.
Gründe
String.Concat
ist viel schneller? Nun, wir können alle sehen, wasString.Format
passiert - Sie werden überrascht sein, wie viel Code für die Verarbeitung der Formatzeichenfolge erforderlich ist. Darüber hinaus (ich habe Kommentare zum Speicherverbrauch gesehen) wirdString.Format
intern ein StringBuilder verwendet. Hier ist wie:StringBuilder builder = new StringBuilder(format.Length + (args.Length * 8));
Für jedes übergebene Argument werden 8 Zeichen reserviert. Wenn das Argument ein einstelliger Wert ist, haben wir schade Platz verschwendet. Wenn es sich bei dem Argument um ein benutzerdefiniertes Objekt handelt, für das Langtext zurückgegeben wird
ToString()
, ist möglicherweise sogar eine Neuzuweisung erforderlich (natürlich im schlimmsten Fall).Im Vergleich dazu verschwendet die Verkettung nur den Speicherplatz des Objektarrays (nicht zu viel, wenn man bedenkt, dass es sich um ein Array von Referenzen handelt). Es gibt keine Analyse für Formatspezifizierer und keinen zwischengeschalteten StringBuilder. Der Box- / Unboxing-Overhead ist bei beiden Methoden vorhanden.
Der einzige Grund, warum ich mich für String.Format entschieden habe, ist die Lokalisierung. Durch das Einfügen von Formatzeichenfolgen in Ressourcen können Sie verschiedene Sprachen unterstützen, ohne den Code zu beeinträchtigen (denken Sie an Szenarien, in denen formatierte Werte die Reihenfolge je nach Sprache ändern, dh "nach {0} Stunden und {1} Minuten" auf Japanisch möglicherweise ganz anders aussehen: ).
Um meinen ersten (und ziemlich langen) Beitrag zusammenzufassen:
ToString()
AufrufeToString()
rufen Sie selbst an, um das Boxen zu vermeiden (ich bin etwas voreingenommen gegenüber der Lesbarkeit) - genau wie bei der ersten Option in Ihrer FrageString.Format()
hat dies eine Kante.quelle
string.Format
ist "sicher" bei Verwendung von ReSharper; Das heißt, es ist so sicher wie jeder andere Code, der [falsch] verwendet werden kann. 2)string.Format
nicht erlauben , für einen „sicheren“null
:string.Format("A{0}B", (string)null)
Ergebnisse in „AB“. 3) Ich kümmere mich selten um dieses Leistungsniveau (und zu diesem Zweck ist es ein seltener Tag, an dem ich michStringBuilder
string s = "This " + MyMethod(arg) + " is a test";
zu einemString.Concat()
Aufruf kompiliert werden.Ich denke, die erste Option ist besser lesbar und das sollte Ihr Hauptanliegen sein.
string.Format verwendet einen StringBuilder unter der Haube (mit Reflektor prüfen ), sodass er keinen Leistungsvorteil bietet , es sei denn, Sie führen eine erhebliche Verkettung durch. Es wird für Ihr Szenario langsamer sein, aber die Realität ist, dass diese Entscheidung zur Optimierung der Mikroleistung die meiste Zeit unangemessen ist und Sie sich wirklich auf die Lesbarkeit Ihres Codes konzentrieren sollten, es sei denn, Sie befinden sich in einer Schleife.
Schreiben Sie in jedem Fall zuerst zur besseren Lesbarkeit und verwenden Sie dann einen Leistungsprofiler , um Ihre Hotspots zu identifizieren, wenn Sie wirklich glauben, dass Sie Leistungsprobleme haben.
quelle
Für einen einfachen Fall , in dem es eine einfache einzelne Verkettung ist, glaube ich , dass es die Komplexität des nicht wert ist
string.Format
(und ich habe nicht getestet, aber ich vermute , dass für einen einfachen Fall wie diesen,string.Format
vielleicht etwas langsamer sein, was mit dem Format - String - Parsing und alles). Wie Jon Skeet ziehe ich es vor, nicht explizit aufzurufen.ToString()
, da dies implizit durch diestring.Concat(string, object)
Überlastung erfolgt und ich denke, dass der Code sauberer aussieht und ohne ihn einfacher zu lesen ist.Aber für mehr als ein paar Verkettungen (wie viele sind subjektiv) bevorzuge ich definitiv
string.Format
. Ab einem bestimmten Punkt denke ich, dass sowohl die Lesbarkeit als auch die Leistung unnötig unter der Verkettung leiden.Wenn die Formatzeichenfolge viele Parameter enthält (wiederum ist "viele" subjektiv), ziehe ich es normalerweise vor, kommentierte Indizes in die Ersetzungsargumente aufzunehmen, damit ich nicht den Überblick verliere, welcher Wert zu welchem Parameter gehört. Ein erfundenes Beispiel:
Aktualisieren
Mir fällt auf, dass das Beispiel, das ich gegeben habe, etwas verwirrend ist, weil es den Anschein hat, dass ich sowohl die Verkettung als auch
string.Format
hier verwendet habe. Und ja, logisch und lexikalisch habe ich das getan. Die Verkettungen werden jedoch alle vom Compiler 1 optimiert , da sie alle Zeichenfolgenliterale sind. Zur Laufzeit gibt es also eine einzelne Zeichenfolge. Ich sollte also sagen, dass ich es vorziehen würde, viele Verkettungen zur Laufzeit zu vermeiden .Natürlich ist der größte Teil dieses Themas nicht mehr aktuell, es sei denn, Sie verwenden noch C # 5 oder älter. Jetzt haben wir Zeichenfolgen interpoliert , die aus Gründen der Lesbarkeit
string.Format
in fast allen Fällen weit überlegen sind. Heutzutage verwende ich fast immer die Zeichenfolgeninterpolation, es sei denn, ich verkette einen Wert direkt mit dem Anfang oder Ende eines Zeichenfolgenliteral. Heute würde ich mein früheres Beispiel so schreiben:Auf diese Weise verlieren Sie die Verkettung während der Kompilierung. Jede interpolierte Zeichenfolge wird
string.Format
vom Compiler in einen Aufruf umgewandelt , und ihre Ergebnisse werden zur Laufzeit verkettet. Dies bedeutet, dass dies ein Opfer der Laufzeitleistung für die Lesbarkeit ist. Meistens ist es ein lohnendes Opfer, da die Laufzeitstrafe vernachlässigbar ist. In leistungskritischem Code müssen Sie jedoch möglicherweise unterschiedliche Lösungen profilieren.1 Sie können dies in der C # -Spezifikation sehen :
Sie können es auch mit einem kleinen Code überprüfen:
quelle
String.Format()
Wenn Ihre Zeichenfolge komplexer wäre und viele Variablen verkettet würden, würde ich die Zeichenfolge auswählen.Format (). Aber für die Größe der Zeichenfolge und die Anzahl der Variablen, die in Ihrem Fall verkettet werden, würde ich mich für Ihre erste Version entscheiden, sie ist spartanischer .
quelle
Ich habe mir String.Format (mit Reflector) angesehen und es erstellt tatsächlich einen StringBuilder und ruft dann AppendFormat darauf auf. So ist es schneller als concat für mehrere stirngs. Am schnellsten (glaube ich) wäre es, einen StringBuilder zu erstellen und die Aufrufe zum manuellen Anhängen auszuführen. Natürlich kann die Anzahl der "vielen" erraten werden. Ich würde + (eigentlich & weil ich meistens ein VB-Programmierer bin) für etwas so Einfaches wie Ihr Beispiel verwenden. Da es immer komplexer wird, verwende ich String.Format. Wenn es VIELE Variablen gibt, würde ich mich für einen StringBuilder entscheiden und anhängen. Zum Beispiel haben wir Code, der Code erstellt. Dort verwende ich eine Zeile tatsächlichen Codes, um eine Zeile generierten Codes auszugeben.
Es scheint einige Spekulationen darüber zu geben, wie viele Zeichenfolgen für jede dieser Operationen erstellt werden. Nehmen wir also einige einfache Beispiele.
"C" ist bereits eine Zeichenfolge.
rowIndex.ToString () erstellt eine weitere Zeichenfolge. (@manohard - es wird kein Boxing von rowIndex stattfinden)
Dann erhalten wir die endgültige Zeichenfolge.
Wenn wir das Beispiel von nehmen
dann haben wir "C {0}" als Zeichenfolge
rowIndex wird eingerahmt, um an die Funktion übergeben zu werden.
Ein neuer Zeichenfolgengenerator wird erstellt.
AppendFormat wird im Zeichenfolgengenerator aufgerufen. Ich kenne die Details der Funktionsweise von AppendFormat nicht, nehme aber an, dass dies der Fall ist Sehr effizient, es wird immer noch erforderlich sein, den boxed rowIndex in einen String umzuwandeln.
Konvertieren Sie dann den Stringbuilder in einen neuen String.
Ich weiß, dass StringBuilder versuchen, sinnlose Speicherkopien zu verhindern, aber das String.Format hat im Vergleich zur einfachen Verkettung immer noch zusätzlichen Aufwand.
Nehmen wir jetzt ein Beispiel mit ein paar weiteren Zeichenfolgen
Wir haben zunächst 6 Zeichenfolgen, die in allen Fällen gleich sind.
Mit der Verkettung haben wir auch 4 Zwischenzeichenfolgen plus das Endergebnis. Es sind diese Zwischenergebnisse, die mithilfe von String, Format (oder einem StringBuilder) eliminiert werden.
Denken Sie daran, dass zum Erstellen jeder Zwischenzeichenfolge die vorherige an einen neuen Speicherort kopiert werden muss. Nicht nur die Speicherzuordnung ist möglicherweise langsam.
quelle
Ich mag String.Format, weil Ihr formatierter Text viel einfacher zu verfolgen und zu lesen ist als die Inline-Verkettung. Außerdem ist er viel flexibler, sodass Sie Ihre Parameter formatieren können. Für kurze Verwendungszwecke wie Ihren sehe ich jedoch kein Problem mit der Verkettung.
Bei Verkettungen innerhalb von Schleifen oder in großen Zeichenfolgen sollten Sie immer versuchen, die StringBuilder-Klasse zu verwenden.
quelle
Dieses Beispiel ist wahrscheinlich zu trivial, um einen Unterschied zu bemerken. Tatsächlich denke ich, dass der Compiler in den meisten Fällen jeden Unterschied überhaupt optimieren kann.
Wenn ich jedoch raten müsste, würde ich
string.Format()
einen Vorteil für kompliziertere Szenarien bieten. Aber das ist eher ein Bauchgefühl, dass es wahrscheinlich besser ist, einen Puffer zu verwenden, anstatt mehrere unveränderliche Zeichenfolgen zu erzeugen, und nicht auf realen Daten basiert.quelle
Ich stimme vielen der oben genannten Punkte zu. Ein weiterer Punkt, von dem ich glaube, dass er erwähnt werden sollte, ist die Wartbarkeit des Codes. string.Format erleichtert das Ändern von Code.
dh ich habe eine Nachricht
"The user is not authorized for location " + location
oder"The User is not authorized for location {0}"
wenn ich jemals die Nachricht ändern wollte, um zu sagen:
location + " does not allow this User Access"
oder"{0} does not allow this User Access"
mit string.Format muss ich nur den String ändern. Zur Verkettung muss ich diese Nachricht ändern
Bei Verwendung an mehreren Orten kann viel Zeit gespart werden.
quelle
Ich hatte den Eindruck, dass string.format schneller war und in diesem Test 3 x langsamer zu sein scheint
string.format dauerte 4,6 Sekunden und bei Verwendung von '+' 1,6 Sekunden.
quelle
"1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10"
als ein String-Literal, so dass die Zeile effektiv"12345678910" + i
schneller wird als die vorherigestring.Format(...)
string.Format ist wahrscheinlich die bessere Wahl, wenn die Formatvorlage ("C {0}") in einer Konfigurationsdatei (z. B. Web.config / App.config) gespeichert ist.
quelle
Ich habe ein bisschen verschiedene Zeichenfolgenmethoden profiliert, einschließlich string.Format, StringBuilder und String-Verkettung. Die Verkettung von Zeichenfolgen übertraf fast immer die anderen Methoden zum Erstellen von Zeichenfolgen. Wenn also Leistung der Schlüssel ist, ist es besser. Wenn die Leistung jedoch nicht kritisch ist, finde ich string.Format im Code einfacher zu befolgen. (Aber das ist ein subjektiver Grund) StringBuilder ist jedoch wahrscheinlich am effizientesten in Bezug auf die Speichernutzung.
quelle
Ich bevorzuge String.Format in Bezug auf die Leistung
quelle
Die Verkettung von Zeichenfolgen benötigt im Vergleich zu String.Format mehr Speicher. Die beste Möglichkeit, Zeichenfolgen zu verketten, ist die Verwendung von String.Format oder System.Text.StringBuilder Object.
Nehmen wir den ersten Fall: "C" + rowIndex.ToString () Nehmen wir an, dass rowIndex ein Werttyp ist, sodass die ToString () -Methode Box muss, um den Wert in String zu konvertieren, und dann erstellt CLR Speicher für den neuen String, wobei beide Werte enthalten sind.
Wobei als Zeichenfolge.Format Objektparameter erwartet und rowIndex als Objekt aufnimmt und es intern natürlich in Zeichenfolge konvertiert, wird es Boxing geben, aber es ist intrinsisch und es wird auch nicht so viel Speicher beanspruchen wie im ersten Fall.
Für kurze Saiten ist es nicht so wichtig, denke ich ...
quelle