จะสร้างวัตถุ String จำนวนเท่าใดเมื่อใช้เครื่องหมายบวก


115

จะสร้างวัตถุสตริงจำนวนเท่าใดเมื่อใช้เครื่องหมายบวกในโค้ดด้านล่าง

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

ถ้าเป็นดังด้านล่างนี้ผมจะพูดว่า String object 3 ตัว: "1", "2", "12"

String result = "1" + "2";

ฉันรู้ด้วยว่าอ็อบเจ็กต์ String ถูกแคชไว้ใน String Intern Pool / Table เพื่อการปรับปรุงประสิทธิภาพ แต่นั่นไม่ใช่คำถาม


สตริงจะถูก จำกัด เฉพาะในกรณีที่คุณเรียกใช้ String.Intern อย่างชัดเจน
Joe White

7
@ โจไวท์: เหรอ?
Igor Korkhov

13
ไม่มาก ตัวอักษรสตริงทั้งหมดจะอยู่ภายในโดยอัตโนมัติ ผลลัพธ์ของการดำเนินการสตริงไม่ใช่
Stefan Paul Noack

ยิ่งไปกว่านั้นในตัวอย่าง OP มีค่าคงที่สตริงเพียงค่าเดียวและมีค่าคงที่ ฉันจะอัปเดตคำตอบเพื่อเป็นตัวอย่าง
Chris Shain

+1 สำหรับตัวอย่างในชีวิตจริงของความจำเป็นในการเขียนโค้ด catenation สตริงในสไตล์นั้นส่วนตัวอย่างของmsdn.microsoft.com/en-us/library/…มีอันที่จะเป็นไปไม่ได้หากคอมไพเลอร์ไม่สามารถปรับให้เหมาะสมได้ เป็นค่าคงที่เดียวเนื่องจากข้อ จำกัด เกี่ยวกับค่าที่กำหนดให้กับพารามิเตอร์แอตทริบิวต์
ClickRick

คำตอบ:


161

น่าแปลกที่มันขึ้นอยู่กับ

หากคุณทำสิ่งนี้ด้วยวิธีการ:

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

จากนั้นคอมไพเลอร์ดูเหมือนจะปล่อยรหัสโดยใช้String.Concatตามที่ @Joachim ตอบ (+1 ถึงเขา btw)

หากคุณกำหนดให้เป็นค่าคงที่เช่น:

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

หรือเป็นตัวอักษรเหมือนในคำถามเดิม:

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

จากนั้นคอมไพเลอร์จะปรับ+สัญญาณเหล่านั้นให้เหมาะสม เทียบเท่ากับ:

const String result = "1234";

นอกจากนี้คอมไพลเลอร์จะลบนิพจน์ค่าคงที่ภายนอกและจะปล่อยออกมาหากมีการใช้หรือเปิดเผยเท่านั้น ตัวอย่างเช่นโปรแกรมนี้:

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

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

สร้างเพียงสตริงเดียว - ค่าคงที่result(เท่ากับ "1234") oneและtwoไม่ปรากฏใน IL ที่เป็นผลลัพธ์

โปรดทราบว่าอาจมีการเพิ่มประสิทธิภาพเพิ่มเติมในขณะรันไทม์ ฉันแค่ไปตามสิ่งที่ IL ผลิตขึ้น

สุดท้ายเกี่ยวกับภายในค่าคงที่และตัวอักษรจะอยู่ภายใน แต่ค่าที่อยู่ภายในคือค่าคงที่ที่เป็นผลลัพธ์ใน IL ไม่ใช่ค่าตัวอักษร ซึ่งหมายความว่าคุณอาจได้รับวัตถุสตริงน้อยกว่าที่คุณคาดหวังเนื่องจากค่าคงที่หรือตัวอักษรที่กำหนดเหมือนกันหลายตัวจะเป็นวัตถุเดียวกัน! นี่คือภาพประกอบดังต่อไปนี้:

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

ในกรณีที่สตริงถูกเชื่อมต่อกันในลูป (หรือแบบไดนามิก) คุณจะได้สตริงพิเศษหนึ่งสตริงต่อการเรียงต่อกัน ตัวอย่างเช่นต่อไปนี้จะสร้างอินสแตนซ์สตริง 12 รายการ: 2 ค่าคงที่ + การวนซ้ำ 10 ครั้งซึ่งแต่ละครั้งจะทำให้เกิดอินสแตนซ์สตริงใหม่:

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

แต่ (ก็น่าแปลกใจเช่นกัน) คอมไพเลอร์ต่อเนื่องกันหลายชุดจะถูกรวมเข้าด้วยกันในการเรียงต่อกันแบบหลายสตริงเดียว ตัวอย่างเช่นโปรแกรมนี้ยังสร้างอินสแตนซ์สตริงได้เพียง 12 ชุดเท่านั้น! เนื่องจาก " แม้ว่าคุณจะใช้ตัวดำเนินการ + หลายตัวในคำสั่งเดียวเนื้อหาสตริงจะถูกคัดลอกเพียงครั้งเดียว "

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

แล้ว String result = "1" + "2" + three + four; โดยที่สองและสามประกาศเหมือนสตริงที่สาม = "3"; สตริงที่สี่ = "4";?
The Light

แม้ผลลัพธ์จะเป็นสตริงเดียว ฉันเพิ่งใช้งานผ่าน LinqPad เพื่อตรวจสอบตัวเองอีกครั้ง
Chris Shain

1
@Servy - ความคิดเห็นดูเหมือนจะได้รับการอัปเดตแล้ว เมื่อคุณเปลี่ยนความคิดเห็นจะไม่มีการทำเครื่องหมายว่ามีการเปลี่ยนแปลง
Security Hound

1
กรณีหนึ่งที่ควรพิจารณาเพื่อความสมบูรณ์คือการต่อกันเป็นวง เช่นจำนวนสตริงอ็อบเจ็กต์ที่จัดสรรโค้ดต่อไปนี้:string s = ""; for (int i = 0; i < n; i++) s += "a";
Joren

1
ฉันใช้ LINQPad ( linqpad.net ) หรือ Reflector ( reflector.net ) ก่อนหน้านี้แสดงให้คุณเห็น IL ของตัวอย่างโค้ดโดยพลการส่วนหลังจะถอดรหัสประกอบเป็น IL และสามารถสร้าง C # ที่เทียบเท่าใหม่จาก IL นั้นได้ นอกจากนี้ยังมีเครื่องมือในตัวที่เรียกว่า ILDASM ( msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx ) การทำความเข้าใจ IL เป็นเรื่องยุ่งยาก - ดูcodebetter.com/raymondlewallen/2005/ 02/07 / …
Chris Shain

85

คำตอบของ Chris Shain นั้นดีมาก ในฐานะคนที่เขียนเครื่องมือเพิ่มประสิทธิภาพการต่อสายอักขระฉันจะเพิ่มประเด็นที่น่าสนใจอีกสองประเด็น

ประการแรกคือเครื่องมือเพิ่มประสิทธิภาพการเชื่อมต่อโดยหลักแล้วจะละเว้นทั้งวงเล็บและการเชื่อมโยงด้านซ้ายเมื่อทำได้อย่างปลอดภัย สมมติว่าคุณมีเมธอด M () ที่ส่งคืนสตริง ถ้าคุณพูด:

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

จากนั้นคอมไพเลอร์ให้เหตุผลว่าตัวดำเนินการเพิ่มเติมถูกปล่อยให้เชื่อมโยงดังนั้นนี่จึงเหมือนกับ:

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

แต่นี่:

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

เหมือนกับ

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

เพื่อให้เป็น concatenation ของสตริงอย่างต่อเนื่อง กับ"CD"M()

ในความเป็นจริงเครื่องมือเพิ่มประสิทธิภาพการเรียงต่อกันตระหนักดีว่าการต่อสายอักขระเป็นการเชื่อมโยงและสร้างขึ้นString.Concat(M(), "AB")สำหรับตัวอย่างแรกแม้ว่าจะละเมิดการเชื่อมโยงด้านซ้ายก็ตาม

คุณสามารถทำได้:

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

String.Concat(M(), "EF", M())และเราจะยังคงสร้าง

จุดที่น่าสนใจประการที่สองคือสตริงว่างและสตริงว่างจะถูกปรับให้เหมาะสม ดังนั้นหากคุณทำสิ่งนี้:

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

คุณจะได้รับ String.Concat(M(), M())

คำถามที่น่าสนใจก็เกิดขึ้น: แล้วสิ่งนี้ล่ะ?

string s = M() + null;

เราไม่สามารถปรับให้เหมาะสมลงไปได้

string s = M();

เนื่องจากM()อาจส่งคืนค่าว่าง แต่String.Concat(M(), null)จะส่งคืนสตริงว่างหากM()ส่งคืนค่าว่าง ดังนั้นสิ่งที่เราทำคือแทนที่จะลด

string s = M() + null;

ถึง

string s = M() ?? "";

ดังนั้นจึงแสดงให้เห็นว่าการต่อสายอักขระไม่จำเป็นต้องเรียกString.Concatเลย

สำหรับการอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้โปรดดู

เหตุใด String.Concat จึงไม่ได้รับการปรับให้เหมาะกับ StringBuilder.Append


ฉันคิดว่าอาจมีข้อผิดพลาดบางประการเกิดขึ้นที่นั่น แท้จริง("C" + "D") + M())สร้างไม่ได้String.Concat("CD", M()) String.Concat(M(), "AB")และลงต่อไป(M() + "E") + (null + M())ควรจะสร้างไม่ได้String.Concat(M(), "E", M()) String.Concat(M(), M())
hammar

21
+1 สำหรับย่อหน้าเริ่มต้น :) คำตอบเช่นนี้เป็นสิ่งที่ทำให้ฉันประหลาดใจเสมอเกี่ยวกับ Stack Overflow
brichins

23

ฉันพบคำตอบที่ MSDN หนึ่ง.

วิธีการ: เชื่อมต่อหลายสตริง (คู่มือการเขียนโปรแกรม C #)

การเชื่อมต่อเป็นกระบวนการต่อท้ายสตริงหนึ่งไปยังส่วนท้ายของสตริงอื่น เมื่อคุณเชื่อมต่อตัวอักษรสตริงหรือค่าคงที่ของสตริงโดยใช้ตัวดำเนินการ + คอมไพเลอร์จะสร้างสตริงเดียว ไม่มีการเชื่อมต่อเวลาทำงาน อย่างไรก็ตามตัวแปรสตริงสามารถเชื่อมต่อกันได้ในขณะรันไทม์เท่านั้น ในกรณีนี้คุณควรเข้าใจผลกระทบด้านประสิทธิภาพของแนวทางต่างๆ


22

แค่หนึ่ง. คอมไพเลอร์ C # จะพับค่าคงที่ของสตริงและด้วยเหตุนี้คอมไพเลอร์จึงรวบรวมลงไปที่

String result = "1234";

ฉันคิดว่าเมื่อใดก็ตามที่คุณใช้ "" มันจะสร้างวัตถุสตริง
The Light

1
@ วิลเลียมโดยทั่วไปใช่ แต่การพับอย่างต่อเนื่องจะลบขั้นตอนกลางที่ไม่จำเป็นออกไป
JaredPar

13

ฉันสงสัยว่านี่เป็นข้อบังคับมาตรฐานหรือข้อกำหนดใด ๆ เวอร์ชันหนึ่งอาจทำสิ่งที่แตกต่างจากเวอร์ชันอื่นได้


3
เป็นเอกสารพฤติกรรมอย่างน้อยสำหรับคอมไพเลอร์ C # ของ Microsoft สำหรับ VS 2008 และ 2010 (ดูคำตอบของ @ David-Stratton) ที่กล่าวว่าคุณพูดถูก - เท่าที่ฉันสามารถบอกได้จากการตรวจสอบอย่างรวดเร็วข้อมูลจำเพาะ C # ไม่ได้ระบุสิ่งนี้และอาจถือเป็นรายละเอียดการใช้งาน
Chris Shain

13

หนึ่งเนื่องจากเป็นแบบคงที่คอมไพลเลอร์จะสามารถเพิ่มประสิทธิภาพให้เป็นสตริงเดียวได้ในเวลาคอมไพล์

หากพวกเขาได้รับแบบไดนามิกที่พวกเขาต้องการได้รับการปรับปรุงให้สายเดียวที่จะString.Concat (สตริงสตริงสตริงสตริง)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.