Berapa banyak objek String yang akan dibuat saat menggunakan tanda plus?

115

Berapa banyak objek String yang akan dibuat saat menggunakan tanda plus pada kode di bawah ini?

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

Jika seperti di bawah ini, saya akan mengatakan tiga objek String: "1", "2", "12".

String result = "1" + "2";

Saya juga tahu bahwa objek String di-cache di String Intern Pool / Table untuk peningkatan kinerja, tapi bukan itu pertanyaannya.

Cahaya
sumber
String hanya disimpan jika Anda memanggil String.Intern secara eksplisit.
Joe White
7
@ JoeWhite: apakah mereka?
Igor Korkhov
13
Tidak terlalu. Semua literal string disimpan secara otomatis. Hasil dari operasi string tidak.
Stefan Paul Noack
Terlebih lagi, dalam contoh OP, hanya ada satu konstanta string, dan itu internal. Saya akan memperbarui jawaban saya untuk mengilustrasikan.
Chris Shain
+1. Untuk contoh kehidupan nyata dari kebutuhan untuk mengkodekan catenation string dalam gaya itu, bagian Contoh msdn.microsoft.com/en-us/library/… memiliki satu yang tidak akan mungkin jika kompilator tidak dapat mengoptimalkannya ke satu konstanta, karena batasan pada nilai yang ditetapkan ke parameter atribut.
ClickRick

Jawaban:

161

Anehnya, itu tergantung.

Jika Anda melakukan ini dengan metode:

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

kemudian compiler tampaknya memancarkan kode menggunakan String.Concat@Joachim menjawab (+1 padanya btw).

Jika Anda mendefinisikannya sebagai konstanta , misalnya:

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

atau sebagai literal , seperti pada pertanyaan awal:

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

maka kompilator akan mengoptimalkan +tanda - tanda tersebut. Ini setara dengan:

const String result = "1234";

Selanjutnya, kompilator akan menghapus ekspresi konstanta asing, dan hanya memancarkannya jika digunakan atau diekspos. Misalnya, program ini:

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

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

Hanya menghasilkan satu string- konstanta result(sama dengan "1234"). onedan twotidak muncul di IL yang dihasilkan.

Perlu diingat bahwa mungkin ada pengoptimalan lebih lanjut pada waktu proses. Saya hanya mengikuti apa yang IL diproduksi.

Akhirnya, dalam hal interning, konstanta dan literal diinternir, tetapi nilai yang diinternir adalah nilai konstanta yang dihasilkan di IL, bukan literal. Ini berarti bahwa Anda mungkin mendapatkan lebih sedikit objek string dari yang Anda harapkan, karena beberapa konstanta atau literal yang didefinisikan secara identik sebenarnya akan menjadi objek yang sama! Ini diilustrasikan sebagai berikut:

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();
    }
}

Dalam kasus di mana String digabungkan dalam satu lingkaran (atau sebaliknya secara dinamis), Anda berakhir dengan satu string tambahan per penggabungan. Misalnya, berikut ini membuat 12 instance string: 2 konstanta + 10 iterasi, masing-masing menghasilkan instance String baru:

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

Tetapi (juga secara mengejutkan), beberapa penggabungan yang berurutan digabungkan oleh kompilator menjadi satu penggabungan multi-string. Misalnya, program ini juga hanya menghasilkan 12 instance string! Ini karena " Meskipun Anda menggunakan beberapa + operator dalam satu pernyataan, konten string hanya akan disalin sekali. "

public class Program
{
    static void Main(string[] args)
    {
        string result = "";
        for (int i = 0; i < 10; i++)
            result += "a" + result;
        Console.ReadKey();
    }
}
Chris Shain
sumber
bagaimana dengan hasil String = "1" + "2" + tiga + empat; dimana dua dan tiga dinyatakan seperti string tiga = "3"; String empat = "4" ;?
The Light
Bahkan itu menghasilkan satu string. Saya baru saja menjalankannya melalui LinqPad untuk memeriksa ulang diri saya sendiri.
Chris Shain
1
@Servy - Komentar tampaknya telah diperbarui. Ketika Anda mengubah komentar, itu tidak ditandai sebagai diubah.
Security Hound
1
Satu kasus yang akan bagus untuk dipertimbangkan untuk kelengkapan adalah penggabungan dalam satu lingkaran. Misalnya Berapa banyak objek string yang dialokasikan kode berikut:string s = ""; for (int i = 0; i < n; i++) s += "a";
Joren
1
Saya menggunakan LINQPad ( linqpad.net ) atau Reflector ( reflector.net ). Yang pertama menunjukkan kepada Anda IL dari potongan kode sembarang, yang terakhir mendekompilasi rakitan menjadi IL dan dapat menghasilkan kembali C # yang setara dari IL itu. Ada juga alat bawaan yang disebut ILDASM ( msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx ) Memahami IL adalah hal yang rumit-
Chris Shain
85

Jawaban Chris Shain sangat bagus. Sebagai orang yang menulis pengoptimal rangkaian string, saya hanya akan menambahkan dua poin menarik tambahan.

Yang pertama adalah bahwa pengoptimal penggabungan pada dasarnya mengabaikan tanda kurung dan asosiasi kiri jika dapat melakukannya dengan aman. Misalkan Anda memiliki metode M () yang mengembalikan string. Jika Anda mengatakan:

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

maka kompilator beralasan bahwa operator penjumlahan dibiarkan asosiatif, dan oleh karena itu ini sama dengan:

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

Tapi ini:

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

sama dengan

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

jadi itu adalah penggabungan string konstan "CD" dengan M().

Faktanya, pengoptimal penggabungan menyadari bahwa penggabungan string adalah asosiatif , dan menghasilkan String.Concat(M(), "AB")untuk contoh pertama, meskipun itu melanggar asosiasi kiri.

Anda bahkan dapat melakukan ini:

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

dan kami masih akan menghasilkan String.Concat(M(), "EF", M()).

Hal menarik kedua adalah bahwa string kosong dan kosong dioptimalkan. Jadi jika Anda melakukan ini:

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

Anda akan mendapatkan String.Concat(M(), M())

Sebuah pertanyaan menarik kemudian muncul: bagaimana dengan ini?

string s = M() + null;

Kami tidak bisa mengoptimalkannya hingga

string s = M();

karena M()mungkin mengembalikan null, tetapi String.Concat(M(), null)akan mengembalikan string kosong jika M()mengembalikan null. Jadi yang kita lakukan justru mengurangi

string s = M() + null;

untuk

string s = M() ?? "";

Dengan demikian menunjukkan bahwa penggabungan string sebenarnya tidak perlu dipanggil String.Concatsama sekali.

Untuk bacaan lebih lanjut tentang subjek ini, lihat

Mengapa String.Concat tidak dioptimalkan untuk StringBuilder.Append?

Eric Lippert
sumber
Saya pikir beberapa kesalahan mungkin telah menyelinap di sana. Tentunya, ("C" + "D") + M())menghasilkan String.Concat("CD", M()), tidak String.Concat(M(), "AB"). Dan lebih jauh ke bawah, (M() + "E") + (null + M())harus menghasilkan String.Concat(M(), "E", M()), bukan String.Concat(M(), M()).
hammar
21
1 untuk paragraf awal. :) Jawaban seperti inilah yang selalu membuat saya takjub tentang Stack Overflow.
Brichins
23

Saya menemukan jawabannya di MSDN. Satu.

Cara: Menggabungkan Beberapa String (Panduan Pemrograman C #)

Penggabungan adalah proses menambahkan satu string ke ujung string lain. Saat Anda menggabungkan literal string atau konstanta string dengan menggunakan operator +, kompilator membuat satu string. Tidak ada penggabungan waktu proses yang terjadi. Namun, variabel string hanya dapat digabungkan pada waktu proses. Dalam kasus ini, Anda harus memahami implikasi kinerja dari berbagai pendekatan.

David
sumber
22

Hanya satu. Kompiler C # akan melipat konstanta string dan karenanya pada dasarnya dikompilasi menjadi

String result = "1234";
JaredPar
sumber
Saya pikir setiap kali Anda menggunakan "", itu membuat objek String.
The Light
1
@William secara umum ya. Tapi pelipatan konstan akan menghapus langkah
JaredPar
13

Saya ragu ini diamanatkan oleh standar atau spesifikasi apa pun. Satu versi kemungkinan dapat melakukan sesuatu yang berbeda dari yang lain.

Variabel yang Menyedihkan
sumber
3
Ini adalah perilaku yang didokumentasikan setidaknya untuk kompiler C # Microsoft untuk VS 2008 dan 2010 (lihat jawaban @ David-Stratton). Yang mengatakan, Anda benar- sejauh yang saya tahu dari teliti cepat, spesifikasi C # tidak menentukan ini dan mungkin harus dianggap sebagai detail implementasi.
Chris Shain
13

Pertama, karena bersifat statis, kompilator akan dapat mengoptimalkannya ke satu string pada waktu kompilasi.

Jika mereka dinamis, mereka akan dioptimalkan untuk satu panggilan ke String.Concat (string, string, string, string) .

Joachim Isaksson
sumber