Quanti oggetti String verranno creati quando si utilizza un segno più?


115

Quanti oggetti String verranno creati quando si utilizza un segno più nel codice seguente?

String result = "1" + "2" + "3" + "4";

Se fosse come sotto, avrei detto tre oggetti String: "1", "2", "12".

String result = "1" + "2";

So anche che gli oggetti String vengono memorizzati nella cache nel pool / tabella String Intern per migliorare le prestazioni, ma non è questo il problema.


Le stringhe vengono internate solo se chiami esplicitamente String.Intern.
Joe White

7
@JoeWhite: sono?
Igor Korkhov

13
Non proprio. Tutti i valori letterali stringa vengono internati automaticamente. I risultati delle operazioni sulle stringhe non lo sono.
Stefan Paul Noack

Inoltre, nell'esempio OP, c'è solo una costante di stringa ed è internata. Aggiornerò la mia risposta per illustrare.
Chris Shain

+1. Per un esempio reale della necessità di codificare una catenazione di stringhe in quello stile, la sezione Esempi di msdn.microsoft.com/en-us/library/… ne ha una che non sarebbe possibile se il compilatore non fosse in grado di ottimizzarla a una singola costante, a causa dei vincoli sui valori assegnati ai parametri degli attributi.
ClickRick

Risposte:


161

Sorprendentemente, dipende.

Se lo fai con un metodo:

void Foo() {
    String one = "1";
    String two = "2";
    String result = one + two + "34";
    Console.Out.WriteLine(result);
}

quindi il compilatore sembra emettere il codice usando String.Concatcome ha risposto @Joachim (+1 a lui btw).

Se li definisci come costanti , ad esempio:

const String one = "1";
const String two = "2";
const String result = one + two + "34";

o come letterali , come nella domanda originale:

String result = "1" + "2" + "3" + "4";

quindi il compilatore ottimizzerà quei +segni. È equivalente a:

const String result = "1234";

Inoltre, il compilatore rimuoverà le espressioni costanti estranee e le emetterà solo se vengono utilizzate o esposte. Ad esempio, questo programma:

const String one = "1";
const String two = "1";
const String result = one + two + "34";

public static void main(string[] args) {
    Console.Out.WriteLine(result);
}

Genera solo una stringa, la costante result(uguale a "1234"). onee twonon vengono visualizzati nell'IL risultante.

Tieni presente che potrebbero essere disponibili ulteriori ottimizzazioni in fase di esecuzione. Sto solo seguendo ciò che viene prodotto da IL.

Infine, per quanto riguarda l'interning, le costanti e i letterali sono internati, ma il valore internato è il valore costante risultante nell'IL, non il letterale. Ciò significa che potresti ottenere ancora meno oggetti stringa di quanto ti aspetti, poiché più costanti o valori letterali definiti in modo identico saranno effettivamente lo stesso oggetto! Ciò è illustrato da quanto segue:

public class Program
{
    private const String one = "1";
    private const String two = "2";
    private const String RESULT = one + two + "34";

    static String MakeIt()
    {
        return "1" + "2" + "3" + "4";
    }   

    static void Main(string[] args)
    {
        string result = "1" + "2" + "34";

        // Prints "True"
        Console.Out.WriteLine(Object.ReferenceEquals(result, MakeIt()));

        // Prints "True" also
        Console.Out.WriteLine(Object.ReferenceEquals(result, RESULT));
        Console.ReadKey();
    }
}

Nel caso in cui le stringhe siano concatenate in un ciclo (o in altro modo dinamicamente), si finisce con una stringa in più per concatenazione. Ad esempio, quanto segue crea 12 istanze di stringa: 2 costanti + 10 iterazioni, ciascuna risultante in una nuova istanza di stringa:

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a";
        Console.ReadKey();
    }
}

Ma (anche sorprendentemente), più concatenazioni consecutive vengono combinate dal compilatore in una singola concatenazione multi-stringa. Ad esempio, questo programma produce anche solo 12 istanze di stringa! Questo perché " Anche se usi più + operatori in un'istruzione, il contenuto della stringa viene copiato solo una volta. "

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a" + result;
        Console.ReadKey();
    }
}

che dire del risultato della stringa = "1" + "2" + tre + quattro; dove due e tre sono dichiarati come stringa tre = "3"; Stringa quattro = "4" ;?
The Light

Anche questo si traduce in una stringa. L'ho appena eseguito attraverso LinqPad per ricontrollare me stesso.
Chris Shain

1
@Servy - Il commento sembra essere stato aggiornato. Quando si modifica un commento non viene contrassegnato come modificato.
Segugio della sicurezza

1
Un caso che sarebbe bello considerare per completezza è la concatenazione in un ciclo. Ad esempio, quanti oggetti stringa alloca il codice seguente:string s = ""; for (int i = 0; i < n; i++) s += "a";
Joren

1
Uso LINQPad ( linqpad.net ) o Reflector ( reflector.net ). Il primo mostra l'IL di frammenti di codice arbitrari, il secondo decompila gli assembly in IL e può rigenerare il C # equivalente da quell'IL. C'è anche un built-in strumento denominato ILDASM ( msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx ) Capire IL è un ingannevole cosa- vedere codebetter.com/raymondlewallen/2005/ 07/02 /…
Chris Shain

85

La risposta di Chris Shain è molto buona. In qualità di persona che ha scritto l'ottimizzatore di concatenazione di stringhe, aggiungerei solo due ulteriori punti interessanti.

Il primo è che l'ottimizzatore di concatenazione ignora essenzialmente sia le parentesi che l'associatività a sinistra quando può farlo in modo sicuro. Supponi di avere un metodo M () che restituisce una stringa. Se dici:

string s = M() + "A" + "B";

quindi il compilatore ritiene che l'operatore di addizione sia lasciato associativo, e quindi questo è lo stesso di:

string s = ((M() + "A") + "B");

Ma questo:

string s = "C" + "D" + M();

equivale a

string s = (("C" + "D") + M());

quindi questa è la concatenazione della stringa costante "CD" con M().

In effetti, l'ottimizzatore di concatenazione si rende conto che la concatenazione di stringhe è associativa e genera String.Concat(M(), "AB")per il primo esempio, anche se ciò viola l'associatività a sinistra.

Puoi anche farlo:

string s = (M() + "E") + ("F" + M()));

e genereremo comunque String.Concat(M(), "EF", M()).

Il secondo punto interessante è che le stringhe null e vuote vengono ottimizzate. Quindi se fai questo:

string s = (M() + "") + (null + M());

otterrai String.Concat(M(), M())

Viene quindi sollevata una domanda interessante: che dire di questo?

string s = M() + null;

Non possiamo ottimizzarlo fino a

string s = M();

perché M()potrebbe restituire null, ma String.Concat(M(), null)restituirebbe una stringa vuota se M()restituisse null. Quindi quello che facciamo è invece ridurre

string s = M() + null;

per

string s = M() ?? "";

Dimostrando così che la concatenazione di stringhe non ha bisogno di chiamare String.Concataffatto.

Per ulteriori letture su questo argomento, vedere

Perché String.Concat non è ottimizzato per StringBuilder.Append?


Penso che un paio di errori potrebbero essere scivolati lì dentro. Sicuramente, ("C" + "D") + M())genera String.Concat("CD", M()), no String.Concat(M(), "AB"). E più in basso, (M() + "E") + (null + M())dovrebbe generare String.Concat(M(), "E", M()), no String.Concat(M(), M()).
Hammar

21
+1 per il paragrafo iniziale. :) Risposte come questa sono ciò che mi stupisce sempre di Stack Overflow.
brichins

23

Ho trovato la risposta su MSDN. Uno.

Procedura: concatenare più stringhe (Guida per programmatori C #)

La concatenazione è il processo di aggiunta di una stringa alla fine di un'altra stringa. Quando si concatenano valori letterali stringa o costanti stringa utilizzando l'operatore +, il compilatore crea una singola stringa. Non si verifica alcuna concatenazione in fase di esecuzione. Tuttavia, le variabili stringa possono essere concatenate solo in fase di esecuzione. In questo caso, è necessario comprendere le implicazioni sulle prestazioni dei vari approcci.


22

Solo uno. Il compilatore C # ripiegherà le costanti di stringa e quindi si compila essenzialmente in

String result = "1234";

Ho pensato che ogni volta che usi "", crea un oggetto String.
The Light

1
@ William in generale sì. Ma il piegamento costante rimuoverà i passaggi intermedi non necessari
JaredPar

13

Dubito che ciò sia imposto da qualsiasi standard o specifica. Una versione può probabilmente fare qualcosa di diverso da un'altra.


3
È un comportamento documentato almeno per il compilatore C # di Microsoft per VS 2008 e 2010 (vedere la risposta di @ David-Stratton). Detto questo, hai ragione: per quanto posso dire da una rapida lettura, la specifica C # non lo specifica e probabilmente dovrebbe essere considerato un dettaglio di implementazione.
Chris Shain

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.