Artı işareti kullanılırken kaç String nesnesi oluşturulacak?


115

Aşağıdaki kodda artı işareti kullanıldığında kaç String nesnesi oluşturulacak?

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

Aşağıdaki gibi olsaydı, üç String nesnesi söylerdim: "1", "2", "12".

String result = "1" + "2";

Performans iyileştirmesi için String nesnelerinin String Intern Pool / Table'da önbelleğe alındığını da biliyorum, ancak soru bu değil.


Dizeler yalnızca String.Intern'i açıkça çağırırsanız dahil edilir.
Joe White

7
@JoeWhite: Onlar?
Igor Korkhov

13
Pek değil. Tüm dize değişmezleri otomatik olarak dahil edilir. Dize işlemlerinin sonuçları değildir.
Stefan Paul Noack

Dahası, OP örneğinde yalnızca bir dizge sabiti vardır ve bu sabitlenmiştir. Cevabımı açıklamak için güncelleyeceğim.
Chris Shain

+1. Bu tarzda bir dize katmanı kodlama ihtiyacının gerçek hayattan bir örneği için, msdn.microsoft.com/en-us/library/… ' nin Örnekler bölümünde , derleyicinin optimize edememesi durumunda mümkün olmayacak bir tane vardır. öznitelik parametrelerine atanan değerler üzerindeki kısıtlamalar nedeniyle tek bir sabite.
ClickRick

Yanıtlar:


161

Şaşırtıcı bir şekilde duruma göre değişir.

Bunu bir yöntemle yaparsanız:

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

daha sonra derleyici kodu String.Concat@ Joachim yanıtını kullanarak (ona btw +1) gönderiyor gibi görünüyor .

Bunları sabitler olarak tanımlarsanız , örneğin:

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

veya orijinal soruda olduğu gibi değişmez değerler olarak:

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

daha sonra derleyici bu +işaretleri ortadan kaldıracaktır . Eşdeğeri:

const String result = "1234";

Ayrıca, derleyici yabancı sabit ifadeleri kaldıracak ve yalnızca kullanıldıklarında veya açığa çıktıklarında yayınlayacaktır. Örneğin, bu program:

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

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

Yalnızca bir dize üretir - sabit result("1234" e eşit). oneve twoortaya çıkan IL'de görünmez.

Çalışma zamanında daha fazla optimizasyon olabileceğini unutmayın. Ben sadece IL'nin ürettiği şeyle gidiyorum.

Son olarak, interning ile ilgili olarak, sabitler ve değişmezler dahil edilir, ancak dahili olan değer, IL'deki sonuçta elde edilen sabit değerdir, değişmez değer değil. Bu, beklediğinizden daha az dizi nesnesi elde edebileceğiniz anlamına gelir, çünkü birden fazla aynı tanımlı sabit veya değişmez değer aslında aynı nesne olacaktır! Bu, aşağıdaki şekilde gösterilmektedir:

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

Dizelerin bir döngüde (veya başka bir şekilde dinamik olarak) birleştirilmesi durumunda, bitiştirme başına fazladan bir dize elde edersiniz. Örneğin, aşağıdakiler 12 dize örneği oluşturur: 2 sabit + 10 yineleme, her biri yeni bir String örneğiyle sonuçlanır:

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

Ancak (şaşırtıcı bir şekilde), birden çok ardışık birleştirme, derleyici tarafından tek bir çok dizeli birleştirme halinde birleştirilir. Örneğin, bu program aynı zamanda yalnızca 12 dizgi örneği üretir! Bunun nedeni, " Bir ifadede birden fazla + operatörü kullansanız bile, dize içeriği yalnızca bir kez kopyalanır. "

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

Peki ya String sonucu = "1" + "2" + üç + dört; burada iki ve üç, string üç = "3" gibi bildirilir; Dize dört = "4" ;?
The Light

Bu bile tek bir dizeyle sonuçlanır. Kendimi iki kez kontrol etmek için LinqPad üzerinden yaptım.
Chris Shain

1
@Servy - Yorum güncellenmiş görünüyor. Bir yorumu değiştirdiğinizde, değiştiriliyor olarak işaretlenmez.
Güvenlik Hound

1
Tamlık için dikkate alınması güzel olan bir durum, bir döngüde birleştirmektir. Örneğin, aşağıdaki kod kaç tane string nesnesi string s = ""; for (int i = 0; i < n; i++) s += "a";
ayırır

1
LINQPad ( linqpad.net ) veya Reflector ( reflektör.net ) kullanıyorum. İlki size rastgele kod parçacıklarının IL'sini gösterir, ikincisi derlemeleri IL'ye dönüştürür ve bu IL'den eşdeğer C # üretebilir. ILDASM adlı yerleşik bir araç da vardır ( msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx ) IL'yi
02/07

85

Chris Shain'in cevabı çok güzel. Dize birleştirme iyileştiricisini yazan kişi olarak, sadece iki ilginç nokta daha ekledim.

Birincisi, birleştirme iyileştiricisinin, güvenli bir şekilde yapabildiğinde hem parantezleri hem de sol ilişkilendirilebilirliği esasen yok saymasıdır. Bir dize döndüren bir M () yönteminiz olduğunu varsayalım. Eğer öyle diyorsan:

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

daha sonra derleyici, toplama operatörünün ilişkisel bırakılmasına neden olur ve bu nedenle bu, şununla aynıdır:

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

Ama bu:

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

aynıdır

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

böylece birleşimidir sabit dize "CD" ile M().

Aslında, birleştirme iyileştiricisi, dize birleştirmenin ilişkisel olduğunu fark eder ve String.Concat(M(), "AB")sol ilişkilendirilebilirliği ihlal etse bile ilk örnek için üretir .

Bunu bile yapabilirsiniz:

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

ve yine de üreteceğiz String.Concat(M(), "EF", M()).

İkinci ilginç nokta, boş ve boş dizelerin optimize edilmesidir. Yani bunu yaparsanız:

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

alacaksınız String.Concat(M(), M())

Sonra ilginç bir soru ortaya çıkıyor: buna ne dersiniz?

string s = M() + null;

Bunu optimize edemeyiz

string s = M();

çünkü M()null döndürebilir, ancak null döndürürse String.Concat(M(), null)boş bir dize M()döndürür. Yani yaptığımız şey bunun yerine azaltmak

string s = M() + null;

için

string s = M() ?? "";

Böylece, dizi birleştirme işleminin aslında çağırılmasına gerek olmadığını gösterir String.Concat.

Bu konu hakkında daha fazla bilgi için bkz.

String.Concat neden StringBuilder.Append için optimize edilmedi?


Sanırım oraya birkaç hata düşmüş olabilir. Şüphesiz ("C" + "D") + M())üretir String.Concat("CD", M()), değil String.Concat(M(), "AB"). Ve daha aşağı, (M() + "E") + (null + M())üretmeli String.Concat(M(), "E", M()), değil String.Concat(M(), M()).
hammar

21
Başlangıç ​​paragrafı için +1. :) Bunun gibi yanıtlar, Stack Overflow konusunda beni her zaman şaşırtan şeydir.
brichins

23

Cevabı MSDN'de buldum. Bir.

Nasıl yapılır: Birden Çok Dizeyi Birleştirme (C # Programlama Kılavuzu)

Birleştirme, bir dizeyi başka bir dizenin sonuna ekleme işlemidir. + Operatörünü kullanarak dize değişmezlerini veya dize sabitlerini birleştirdiğinizde, derleyici tek bir dize oluşturur. Çalışma zamanı birleştirme oluşmaz. Bununla birlikte, dize değişkenleri yalnızca çalışma zamanında birleştirilebilir. Bu durumda, çeşitli yaklaşımların performans sonuçlarını anlamalısınız.


22

Sadece bir. C # derleyicisi dizge sabitlerini katlayacak ve bu nedenle esasen

String result = "1234";

"" Her kullandığınızda, bir String nesnesi oluşturduğunu düşündüm.
The Light

1
@William genel olarak evet. Ancak sürekli katlama, gereksiz ara adımları ortadan kaldıracaktır
JaredPar

13

Bunun herhangi bir standart veya spesifikasyon tarafından zorunlu kıldığından şüpheliyim. Bir sürüm muhtemelen diğerinden farklı bir şey yapabilir.


3
En azından Microsoft'un VS 2008 ve 2010 için C # derleyicisi için belgelenmiş davranış (bkz. @ David-Stratton cevabı). Bununla birlikte, haklısınız - hızlı bir incelemeden anlayabildiğim kadarıyla, C # spec bunu belirtmiyor ve muhtemelen bir uygulama ayrıntısı olarak düşünülmelidir.
Chris Shain

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.