ความแตกต่างระหว่างสตริงที่ไม่แน่นอนและไม่เปลี่ยนรูปใน C # คืออะไร?


190

ความแตกต่างระหว่างสตริงที่ไม่แน่นอนและไม่เปลี่ยนรูปใน C # คืออะไร?



6
มันไม่เหมือนกัน คล้ายกันมาก แต่ไม่เหมือนกัน
Marcelo Cantos

2
คำถามที่เกี่ยวข้องเกี่ยวกับ internals ของ StringBuilder stackoverflow.com/q/3564906/38206
Brian Rasmussen

คำตอบ:


313

ไม่แน่นอนและไม่เปลี่ยนรูปเป็นคำภาษาอังกฤษหมายถึง "สามารถเปลี่ยน" และ "ไม่สามารถเปลี่ยน" ตามลำดับ ความหมายของคำนั้นเหมือนกันในบริบทไอที กล่าวคือ

  • สตริงที่ไม่แน่นอนสามารถเปลี่ยนแปลงได้และ
  • ไม่สามารถเปลี่ยนสตริงที่ไม่เปลี่ยนรูปได้

ความหมายของคำเหล่านี้จะเหมือนกันใน C # / .NET เหมือนกับในภาษาโปรแกรม / สภาพแวดล้อมอื่น ๆ แม้ว่าชื่อประเภทนั้นอาจแตกต่างกันออกไป (อย่างชัดเจน) เช่นเดียวกับรายละเอียดอื่น ๆ


สำหรับบันทึก:

  • String คือสตริงสตริงที่ไม่เปลี่ยนรูปแบบมาตรฐาน C # / .Net
  • StringBuilder เป็นชนิดสตริงที่ผันแปรได้ของ C # / .Net มาตรฐาน

ในการ "ทำให้เกิดการเปลี่ยนแปลง" บนสตริงที่แสดงเป็น C # Stringคุณจะต้องสร้างStringวัตถุใหม่ ต้นฉบับStringไม่เปลี่ยนแปลง ... เนื่องจากไม่สามารถเปลี่ยนได้

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


และในที่สุดสำหรับคนเหล่านั้นที่ยืนยันว่า a StringBuilderไม่ใช่สตริงเพราะมันไม่เปลี่ยนรูปเอกสารของ Microsoft อธิบายStringBuilderดังนี้:

"แสดงสตริงของอักขระที่ไม่แน่นอนได้คลาสนี้ไม่สามารถสืบทอดได้"


182

String ไม่เปลี่ยนรูป

เช่นสตริงไม่สามารถแก้ไขได้ เมื่อคุณเปลี่ยนสตริง (โดยการเพิ่มเข้าไป) คุณกำลังสร้างสตริงใหม่

แต่StringBuilderไม่เปลี่ยนแปลงไม่ได้ (ค่อนข้างจะไม่แน่นอน)

ดังนั้นถ้าคุณมีการปรับเปลี่ยนสตริงหลายครั้งเช่น concatenations StringBuilderต่างๆจากนั้นใช้


4
จะเกิดอะไรขึ้นถ้าคุณต่อสายที่ไม่เปลี่ยนรูปหลาย ๆ ครั้ง? ปริมาณการใช้หน่วยความจำมากสำหรับสตริงที่ไม่มีการอ้างอิง? หรือแค่ช้า?
Rudie

13
@Rudie - ทั้งคู่ การต่อข้อมูลแต่ละรายการจะสร้างวัตถุ String ใหม่และคัดลอกอักขระทั้งหมดของทั้งสองสตริงอินพุต นำไปสู่O(N^2)พฤติกรรม ...
สตีเฟนซี

@StephenC อธิบายด้วยคำหลัก
Kent Liau

6
ถ้าคุณต้องเปลี่ยนสตริงหลาย ๆ ครั้งเช่นการเรียงต่อกันหลาย ๆ ครั้งให้ใช้ StringBuilder ... นั่นทำให้ฉันหมดใจโดยสิ้นเชิงขอบคุณ ...
katmanco

45

วัตถุไม่แน่นอนถ้าสร้างขึ้นเมื่อสถานะของมันสามารถเปลี่ยนแปลงได้โดยการเรียกการดำเนินงานต่างๆเกี่ยวกับมันมิฉะนั้นมันจะไม่เปลี่ยนรูป

String ที่ไม่เปลี่ยนรูป

ใน C # (และ. NET) สตริงจะถูกแทนด้วยคลาส System.String stringคำหลักคือนามแฝงสำหรับคลาสนี้

ระดับ System.String จะไม่เปลี่ยนรูปคือเมื่อสร้างแล้วรัฐไม่สามารถเปลี่ยนแปลง

ดังนั้นการดำเนินการทั้งหมดที่คุณดำเนินการในสตริงเหมือนSubstring, Remove, Replace, เรียงต่อกันโดยใช้ '+' ผู้ประกอบการ ฯลฯ จะสร้างสตริงใหม่และส่งกลับมา

ดูโปรแกรมต่อไปนี้สำหรับการสาธิต -

string str = "mystring";
string newString = str.Substring(2);
Console.WriteLine(newString);
Console.WriteLine(str);

สิ่งนี้จะพิมพ์ 'string' และ 'mystring' ตามลำดับ

เพื่อประโยชน์ของการเปลี่ยนแปลงไม่ได้และทำไมสตริงจึงไม่เปลี่ยนรูปเช็คทำไม. NET String ถึงไม่เปลี่ยนรูป? .

สตริงที่ไม่แน่นอน

หากคุณต้องการมีสตริงที่คุณต้องการแก้ไขบ่อยครั้งคุณสามารถใช้StringBuilderคลาส การดำเนินงานใน อินสแตนซ์จะปรับเปลี่ยนวัตถุเดียวกันStringBuilder

สำหรับคำแนะนำเพิ่มเติมเกี่ยวกับการใช้ StringBuilderอ้างอิงถึงเมื่อใดควรใช้ StringBuilder .


คำอธิบายที่สวยงาม!
ฮาริ

36

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

ตรงกันข้ามวัตถุที่เปลี่ยนแปลงไม่ได้มีเขตข้อมูลที่สามารถเปลี่ยนแปลงได้ วิธีการอย่างน้อยหนึ่งอย่างจะเปลี่ยนเนื้อหาของวัตถุหรือมีคุณสมบัติที่เมื่อถูกเขียนเข้าไปจะเปลี่ยนค่าของวัตถุ

หากคุณมีวัตถุที่ไม่แน่นอน - สิ่งที่คล้ายกับ String มากที่สุดคือStringBuffer- คุณต้องทำสำเนาของมันหากคุณต้องการให้แน่ใจว่ามันจะไม่เปลี่ยนแปลงจากคุณ นี่คือเหตุผลที่วัตถุที่เปลี่ยนแปลงไม่ได้มีอันตรายที่จะใช้เป็นกุญแจในรูปแบบใด ๆDictionaryหรือตั้งค่าที่ตัวเองสามารถเปลี่ยนแปลงได้และโครงสร้างข้อมูลจะไม่มีทางรู้ซึ่งนำไปสู่ข้อมูลที่เสียหายซึ่งในที่สุดโปรแกรมของคุณก็จะพัง

อย่างไรก็ตามคุณสามารถเปลี่ยนเนื้อหาได้ดังนั้นจึงมีประสิทธิภาพมากขึ้นกว่าการทำสำเนาเพราะคุณต้องการเปลี่ยนอักขระเดียวหรืออะไรที่คล้ายกัน

โดยทั่วไปสิ่งที่ต้องทำคือใช้วัตถุที่ไม่แน่นอนในขณะที่คุณกำลังสร้างบางสิ่งและวัตถุที่ไม่เปลี่ยนรูปเมื่อคุณทำเสร็จแล้ว สิ่งนี้ใช้กับวัตถุที่มีรูปแบบที่เปลี่ยนแปลงไม่ได้แน่นอน คอลเลกชันส่วนใหญ่ไม่ได้ บ่อยครั้งที่มีประโยชน์ในการจัดหาคอลเลกชันรูปแบบอ่านอย่างเดียวซึ่งเทียบเท่ากับที่ไม่เปลี่ยนรูปเมื่อส่งสถานะภายในของคอลเลกชันของคุณไปยังบริบทอื่น - มิฉะนั้นบางสิ่งบางอย่างอาจใช้ค่าตอบแทนคืนนั้นทำบางสิ่ง ข้อมูล.


12

ไม่เปลี่ยนรูป:

เมื่อคุณทำการดำเนินการบางอย่างกับวัตถุมันจะสร้างวัตถุใหม่ดังนั้นรัฐจะไม่สามารถแก้ไขได้เช่นในกรณีของสตริง

ไม่แน่นอน

เมื่อคุณทำการดำเนินการบางอย่างบนวัตถุวัตถุเองปรับเปลี่ยนไม่สร้าง obect ใหม่เช่นเดียวกับในกรณีของ StringBuilder


8

ใน. NET System.String (สตริง aka) เป็นวัตถุที่ไม่เปลี่ยนรูป ซึ่งหมายความว่าเมื่อคุณสร้างวัตถุคุณจะไม่สามารถเปลี่ยนเป็นค่าได้ในภายหลัง คุณสามารถสร้างวัตถุที่ไม่เปลี่ยนรูปแบบได้เท่านั้น

System.Text.StringBuilder เทียบเท่ากับ System.String ที่ไม่แน่นอนและคุณสามารถเปลี่ยนค่าของมัน

ตัวอย่างเช่น:

class Program
{
    static void Main(string[] args)
    {

        System.String str = "inital value";
        str = "\nsecond value";
        str = "\nthird value";

        StringBuilder sb = new StringBuilder();
        sb.Append("initial value");
        sb.AppendLine("second value");
        sb.AppendLine("third value");
    }
}

สร้าง MSIL ต่อไปนี้: หากคุณตรวจสอบรหัส คุณจะเห็นว่าเมื่อใดก็ตามที่คุณ chane วัตถุของ System.String คุณกำลังสร้างใหม่จริง ๆ แต่ใน System.Text.StringBuilder เมื่อใดก็ตามที่คุณเปลี่ยนค่าของข้อความคุณไม่ต้องสร้างวัตถุใหม่

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       62 (0x3e)
  .maxstack  2
  .locals init ([0] string str,
           [1] class [mscorlib]System.Text.StringBuilder sb)
  IL_0000:  nop
  IL_0001:  ldstr      "inital value"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "\nsecond value"
  IL_000c:  stloc.0
  IL_000d:  ldstr      "\nthird value"
  IL_0012:  stloc.0
  IL_0013:  newobj     instance void [mscorlib]System.Text.StringBuilder::.ctor()
  IL_0018:  stloc.1
  IL_0019:  ldloc.1
  IL_001a:  ldstr      "initial value"
  IL_001f:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string)
  IL_0024:  pop
  IL_0025:  ldloc.1
  IL_0026:  ldstr      "second value"
  IL_002b:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_0030:  pop
  IL_0031:  ldloc.1
  IL_0032:  ldstr      "third value"
  IL_0037:  callvirt   instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
  IL_003c:  pop
  IL_003d:  ret
} // end of method Program::Main

5
อืมม ... รหัสที่แยกส่วนนั้นแสดงให้เห็นเท่านั้นว่ากำลังทำการกำหนดตัวชี้และวิธีการนั้นถูกเรียก มันไม่มีส่วนเกี่ยวข้องกับการเปลี่ยนแปลงและไม่เปลี่ยนรูป (มันทำให้ฉันเข้าใจว่าทำไมบางคนคิดว่าการแยกส่วนรหัสตัวอย่างที่ไม่เกี่ยวข้องออกไปนั้นจะช่วยให้ผู้คนเข้าใจสิ่งนี้ได้สำหรับการเริ่มต้นโปรแกรมเมอร์ส่วนใหญ่ไม่คล่องแคล่วในรหัส CLI)
สตีเฟนซี

6

สตริงไม่แน่นอนเนื่องจาก. NET ใช้สตริงพูลเบื้องหลัง มันหมายถึง:

string name = "My Country";
string name2 = "My Country";

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

name2 = "My Loving Country";

มันจะดูในสตริงพูลสำหรับสตริง "My Loving Country" หากพบว่าคุณจะได้รับการอ้างอิงสตริงใหม่อื่น ๆ ที่ชาญฉลาด "My Loving Country" จะถูกสร้างขึ้นในสตริงพูลและ name2 จะได้รับการอ้างอิงจากมัน แต่กระบวนการทั้งหมดนี้ " ประเทศของฉัน " ไม่ได้เปลี่ยนเพราะตัวแปรอื่น ๆ เช่นชื่อยังคงใช้อยู่ และนั่นคือเหตุผลที่ว่าทำไมสตริงมีไม่เปลี่ยนรูป

StringBuilderทำงานในลักษณะที่แตกต่างกันและไม่ใช้กลุ่มสตริง เมื่อเราสร้างตัวอย่างของ StringBuilder:

var address  = new StringBuilder(500);

มันจัดสรรหน่วยความจำอันขนาด 500 ไบต์สำหรับอินสแตนซ์นี้และการดำเนินการทั้งหมดเพียงแค่แก้ไขตำแหน่งหน่วยความจำนี้และหน่วยความจำนี้ไม่ได้ใช้ร่วมกับวัตถุอื่น ๆ และนั่นคือเหตุผลที่ว่าทำไมStringBuilderคือไม่แน่นอน

ฉันหวังว่ามันจะช่วย


5

ไม่มีจริง คลาส String ไม่สามารถเปลี่ยนแปลงได้

unsafe
{
    string foo = string.Copy("I am immutable.");
    fixed (char* pChar = foo)
    {
        char* pFoo = pChar;

        pFoo[5] = ' ';
        pFoo[6] = ' ';
    }

    Console.WriteLine(foo); // "I am   mutable."
}

ตรรกะชนิดนี้ทำตลอดเวลาในคลาส String และ StringBuilder พวกเขาเพียงจัดสรรสตริงใหม่ทุกครั้งที่คุณเรียก Concat, Substring และอื่น ๆ และใช้เลขคณิตตัวชี้เพื่อคัดลอกไปยังสตริงใหม่ เงื่อนไขก็ไม่ได้กลายพันธุ์ตัวเองดังนั้นทำไมพวกเขาจึงถือว่า "ไม่เปลี่ยนรูป"


โดยวิธีการที่ไม่ได้พยายามนี้กับตัวอักษรของสตริงหรือคุณจะไม่ดีเลอะโปรแกรมของคุณ:

string bar = "I am a string.";

fixed (char* pChar = bar)
{
    char* pBar = pChar;

    pBar[2] = ' ';
}

string baz = "I am a string.";

Console.WriteLine(baz); // "I  m a string."

นี่คือเนื่องจากตัวอักษรของสตริงจะ interned ในเดสก์ท็อป. NET Framework กล่าวอีกนัยหนึ่งbarและbazชี้ไปที่สตริงเดียวกันที่แน่นอนดังนั้นการกลายพันธุ์หนึ่งจะกลายพันธุ์อื่น ๆ ทั้งหมดนี้เป็นสิ่งที่ดีและสวยงามแม้ว่าคุณจะใช้แพลตฟอร์มที่ไม่มีการจัดการเช่น WinRT ซึ่งขาดสตริงการฝึกงาน


1
ใช่แล้ว หากคุณฝ่าฝืนกฎโดยการแยก abstractions เปิดที่ควรจะปิดแล้วแทบไม่มีอะไรเปลี่ยนไม่ได้ คุณสามารถทำสิ่งที่คล้ายคลึงใน Java โดยใช้การสะท้อนที่น่ารังเกียจ ... แต่ JLS ระบุว่าพฤติกรรมที่คุณได้รับนั้นไม่ได้กำหนดไว้
สตีเฟ่นซี

2

เพื่อชี้แจงไม่มีสิ่งเช่นสตริงที่ไม่แน่นอนใน C # (หรือ. NET โดยทั่วไป) langues อื่น ๆ สนับสนุนสตริงที่ไม่แน่นอน (สตริงซึ่งสามารถเปลี่ยนแปลงได้) แต่. NET Framework ไม่ได้

ดังนั้นคำตอบที่ถูกต้องสำหรับคำถามของคุณคือสตริงทั้งหมดไม่เปลี่ยนรูปใน C #

สตริงมีความหมายเฉพาะ คีย์เวิร์ด "string" ตัวพิมพ์เล็กเป็นเพียงทางลัดสำหรับวัตถุที่สร้างอินสแตนซ์จากคลาส System.String วัตถุทั้งหมดที่สร้างจากคลาสสตริงจะไม่เปลี่ยนรูปเสมอ

ถ้าคุณต้องการการแสดงข้อความที่ไม่แน่นอนคุณต้องใช้คลาสอื่นเช่น StringBuilder StringBuilder ช่วยให้คุณสามารถสร้างคอลเลกชันของ 'words' ซ้ำแล้วแปลงเป็นสตริง (เปลี่ยนรูปอีกครั้ง)


ขออภัย แต่เอกสารของ Microsoft สำหรับStringBuilderข้อขัดแย้งนี้: ดูmsdn.microsoft.com/en-us/library/ …
Stephen C

1

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


1

ในรายละเอียดการใช้งาน

System.String ของ CLR2 นั้นไม่แน่นอน StringBuilder.Append โทร String.AppendInplace (วิธีส่วนตัว)

System.String ของ CLR4 นั้นไม่เปลี่ยนรูป StringBuilder มีอาร์เรย์ Char พร้อมกับ chunking


0

จากhttp://yassershaikh.com/what-is-the-difference-between-strings-and-stringbuilder-in-c-net/

คำตอบสั้น ๆ : สตริงไม่เปลี่ยนรูป - ในขณะที่ StringBuilder ไม่แน่นอน

นั่นหมายความว่าอย่างไร ? Wiki พูดว่า: ในเชิงวัตถุวัตถุที่ไม่เปลี่ยนรูปนั้นเป็นวัตถุที่ไม่สามารถแก้ไขสถานะได้หลังจากสร้างขึ้นแล้ว นี่คือตรงกันข้ามกับวัตถุที่ไม่แน่นอนซึ่งสามารถแก้ไขได้หลังจากที่มันถูกสร้างขึ้น

จากเอกสาร StringBuilder Class:

วัตถุ String ไม่เปลี่ยนรูป ทุกครั้งที่คุณใช้วิธีใดวิธีหนึ่งในคลาส System.String คุณสร้างวัตถุสตริงใหม่ในหน่วยความจำซึ่งต้องการการจัดสรรพื้นที่ใหม่สำหรับวัตถุใหม่นั้น

ในสถานการณ์ที่คุณต้องทำการแก้ไขสตริงซ้ำ ๆ ค่าใช้จ่ายที่เกี่ยวข้องกับการสร้างวัตถุ String ใหม่อาจมีค่าใช้จ่ายสูง

คลาส System.Text.StringBuilder สามารถใช้เมื่อคุณต้องการแก้ไขสตริงโดยไม่ต้องสร้างวัตถุใหม่ ตัวอย่างเช่นการใช้คลาส StringBuilder สามารถเพิ่มประสิทธิภาพเมื่อเชื่อมสตริงจำนวนมากเข้าด้วยกันในลูป


0

นี่คือตัวอย่างสำหรับสตริงที่ไม่เปลี่ยนรูปและตัวสร้างสตริงที่ไม่แน่นอน

        Console.WriteLine("Mutable String Builder");
        Console.WriteLine("....................................");
        Console.WriteLine();
        StringBuilder sb = new StringBuilder("Very Good Morning");
        Console.WriteLine(sb.ToString());
        sb.Remove(0, 5);
        Console.WriteLine(sb.ToString());

        Console.WriteLine();

        Console.WriteLine("Immutable String");
        Console.WriteLine("....................................");
        Console.WriteLine();
        string s = "Very Good Morning";
        Console.WriteLine(s);
        s.Substring(0, 5);
        Console.WriteLine(s);
        Console.ReadLine();

มันจะดีมากถ้าคุณสามารถให้ผลผลิตได้เป็นอย่างดี @Gokul :)
ลีรอย

ซับสตริงจะไม่เปลี่ยนค่าพื้นฐาน มันจะส่งกลับค่า
Kye

@Kye และทำไม เพราะสายจะไม่เปลี่ยนรูป :)
LuckyLikey

รหัส 2 บรรทัดของคุณ -: s.Substring (0, 5); Console.WriteLine (s); นี่ s.substring () ไม่เปลี่ยน s ตัวอย่างนี้ไม่แสดงว่าสตริงนั้นไม่เปลี่ยนรูปหรือไม่
Dhananjay

0

สตริงใน C # ไม่เปลี่ยนรูป หากคุณต่อสตริงเข้ากับสตริงใด ๆ แสดงว่าคุณกำลังสร้างสตริงใหม่นั่นคือวัตถุสตริงใหม่! แต่ StringBuilder สร้างสตริงที่ไม่แน่นอน


0

StringBuilder เป็นตัวเลือกที่ดีกว่าในการเชื่อมสตริงข้อมูลขนาดใหญ่เนื่องจาก StringBuilder เป็นประเภทสตริงที่ไม่แน่นอนและวัตถุ StringBuilder เป็นประเภทที่ไม่เปลี่ยนรูปซึ่งหมายความว่า StringBuilder จะไม่สร้างอินสแตนซ์ใหม่ของวัตถุในขณะที่เชื่อมสตริง

ถ้าเราใช้ string แทน StringBuilder เพื่อให้ได้ concatenation มันจะสร้าง instance ใหม่ในหน่วยความจำทุกครั้ง

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