string.format ze zmiennymi vs zmiennymi wbudowanymi

9

Jakie są zalety / wady (jeśli w ogóle) w użyciu

string output; 
int i = 10;
output = string.Format("the int is {0}", i);

przeciw

string output; 
int i = 10;
output = "the int is " + i;

Zawsze korzystałem z tego drugiego przykładu, ale wydaje się, że znaczna większość samouczków online korzysta z przykładu string.format. Nie sądzę, aby istniały jakiekolwiek rzeczywiste różnice pod względem wydajności, początkowo uważam, że koder nie musi przerywać łańcucha, aby wstawiać zmienne.

Jim
źródło
8
Głównym powodem jest to, że znacznie ułatwia tłumaczenie, ponieważ Twój program nie musi rozumieć, w jaki sposób różne języki tworzą zdania. Na przykład wiele wyrażeń i zwrotów w języku francuskim jest odwróconych do siebie w porównaniu do ich angielskich tłumaczeń.
JohnL

Odpowiedzi:

22

Jeśli uważasz, że tłumaczenie jest ważne w twoim projekcie, pierwsza składnia naprawdę w tym pomoże.

Na przykład możesz mieć:

static final string output_en = "{0} is {1} years old.";
static final string output_fr = "{0} a {1} ans.";

int age = 10;
string name = "Henri";
System.out.println(string.Format(output_en, name, age));
System.out.println(string.Format(output_fr, name, age));

Zauważ też, że twoje zmienne nie zawsze mogą znajdować się w tym samym miejscu zdania o tej składni:

static final string output_yoda = "{1} years {0} has.";
Alex Garcia
źródło
4
+1 za użycie Yoda-speak jako przykładu składni czasownika obiektowego.
Mike Harris
1
Z C # mamy nową opcję:System.out.println($"{name} is {age} year's old.");
Berin Loritsch,
@BerinLoritsch: niestety nie nadaje się do lokalizacji.
Bryan Boettcher,
@BryanBoettcher, zrozumiałem, ale nie widziałem nic w OP, mówiąc, że to właśnie starali się osiągnąć.
Berin Loritsch,
8

Sprawdź pierwszą odpowiedź na /programming/4671610/why-use-string-format . Obejmuje wszystko moim zdaniem, dlaczego jest lepiej.

Ponadto każdy zestaw .NET ma pulę wewnętrzną, zawierającą kolekcję unikatowych ciągów. Po skompilowaniu kodu wszystkie literały ciągów, do których odwołujesz się w kodzie, są dodawane do tej puli. Jeśli masz kod, który wygląda tak:

"the int is " + i + " and the double is " + d

To sprawia, że ​​2 ciągi w puli.

Jeśli masz:

"the int is {0} and the double is {1}"

W puli jest tylko jeden ciąg.

Nieco trudniej jest wiedzieć, kiedy ciągi są internowane, a kiedy nie, ponieważ kompilator ma pewną inteligencję podczas wykrywania ciągów, które czasem nie muszą być internowane ... Sprawdź na przykład ten artykuł, który daje więcej informacji na ten temat materia.

Edycja: po nieco wykopaniu napotkałem interesującą odpowiedź na pytanie: Kiedy lepiej jest użyć String.Format vs. konkatenacji ciągu? . Krótko mówiąc, autor odpowiedzi z +30 głosów przedstawia przekonujący argument na rzecz konkatenacji ciągów znaków, gdy lokalizacja nie jest zaangażowana.

Jalayn
źródło
2
Myślę też stylistycznie, że rezonuje z ludźmi tj. Ludźmi takimi jak ja, którzy są przyzwyczajeni do printf i sprintf z c.
Jonathan Henson
Chciałbym wiedzieć, dlaczego głosowanie w celu naprawy mojej odpowiedzi. Dzięki.
Jalayn
4

Wolę pierwszy sposób, ponieważ pozwala mi dokładnie zobaczyć, jak będzie wyglądał łańcuch po wyjściu. Bardzo łatwo jest zapomnieć o dodaniu spacji lub dodatkowym odstępie podczas dodawania ciągów.

Jestem pewien, że pierwszy sposób wiąże się również z poprawą wydajności, ponieważ nie trzeba tworzyć dodatkowych ciągów; ale to nie jest moja główna troska.

John Kraft
źródło
2

Korzystając z pierwszej opcji, możesz przechowywać często używany ciąg formatu i zmniejszyć liczbę potrzebnych operacji pisania oraz ułatwić aktualizację łańcucha wszędzie tam, gdzie jest używany. Zasadniczo pierwsza opcja pozwala na łatwą implementację DRY. Jest to również o wiele ładniejsza składnia, jeśli wiele zmiennych musi być używanych w ciągu, jak wspomniałeś.

Ryathal
źródło
ahh Rozumiem, chyba nie pomyślałem o przykładzie: string.format ("int to {0}. znowu to {0}", int);
Jim
1

Myślę, że dzięki string.Format()temu łatwiej jest zobaczyć, jaki dokładnie będzie wynik (więc nie masz problemów z zapomnianymi spacjami lub czymś takim), a także łatwiej jest pisać i modyfikować.

Jeśli chcesz wykonać bardzo proste formatowanie, użycie +operatora plus może być łatwiejsze, ale zwykle używam go tylko podczas łączenia dwóch ciągów, a nie więcej.

Aby pokazać, jak string.Format()łatwiej jest modyfikować, weź pod uwagę, że chcesz dodać kropkę na końcu zdania w swoim przykładzie: przejście od string.Format("The int is {0}", i)do string.Format("The int is {0}.", i)jest tylko jedną postacią. Ale przejście od "the int is " + ido "the int is " + i + '.'to znacznie więcej.

Kolejną zaletą string.Format()jest to, że pozwala łatwo określić format, na przykład string.Format("The int is 0x{0:X}.", i). Jest to jeszcze ważniejsze przy formatowaniu daty.

Jeśli chodzi o wydajność, string.Format()najprawdopodobniej jest wolniejsza niż proste łączenie łańcuchów. Ale taki kod najprawdopodobniej nie znajduje się na gorącej ścieżce, więc nie ma to znaczenia. A jeśli tak, to prawdopodobnie lepiej jest używać StringBuilder.

svick
źródło
Sformatuj wewnętrznie i tak używa StringBuilder
Bryan Boettcher
1

Użyj tego, który sprawia, że ​​kod jest najbardziej czytelny. Nie martw się o wydajność.

W poniższym przykładzie wolę B, ponieważ jest on po prostu bardziej czytelny. Ale powyższe tłumaczenia językowe również mają sens. Nie pozwól, aby ktokolwiek zmusił cię do użycia łańcucha znaków. Zamiast tego przeczytaj i wskaż doskonały blog Jeffa Atwooda na The Sad Tragedy of Micro Optimizations Theatre

ZA:

string output; 
int i = 10;
output = string.Format("the int is {0}", i);

przeciw

B:

string output; 
int i = 10;
output = "the int is " + i;
Makach
źródło
-1

Ref: Ciąg wyjściowy: format lub konkat w C #?

Rozważ ten kod.

To nieco zmodyfikowana wersja twojego kodu.

  1. Usunąłem Console.WriteLine, ponieważ prawdopodobnie jest on o kilka rzędów wielkości wolniejszy niż to, co próbuję zmierzyć.
  2. Patrzę na stoper przed pętlą i zatrzymuję go zaraz potem, w ten sposób nie tracę precyzji, jeśli funkcja wykonuje na przykład tiki 26,4.
  3. Sposób podziału wyniku przez liczbę iteracji był nieprawidłowy. Zobacz, co się stanie, jeśli masz 1000 milisekund i 100 milisekund. W obu sytuacjach otrzymasz 0 ms po podzieleniu przez 1000000.
Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

Oto moje wyniki:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 618ms - 2213706 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 166ms - 595610 ticks
jp2code
źródło
1
Jak to odpowiada aspektom, jeśli pierwszy przykład kodu lub drugi przykład kodu jest lepszym projektem? W jaki sposób uwzględnia się pół-sekundowe powtórzenie 1M, jeśli jest to łatwiejszy do utrzymania kod dla osoby, czy nie?
Jim zapytał: „Jakie są zalety i wady?” To pokazuje, że w wielu iteracjach String.Format jest szybszy.
jp2code
Powinieneś rozważyć pełne dodanie tego do swojej odpowiedzi, a nie pozostawienie go jako bloku kodu i różnic w stosunku do kodu PO. W tej chwili twoja odpowiedź nie odpowiada na pytanie PO w języku angielskim. Spójrz na inne odpowiedzi. Można by usunąć z nich cały kod i nadal uzyskać odpowiedź na pytanie PO.