Sự khác biệt giữa một chuỗi có thể thay đổi và bất biến trong C # là gì?


190

Sự khác biệt giữa một chuỗi có thể thay đổi và bất biến trong C # là gì?



6
Nó không giống hệt nhau. Rất giống nhau, nhưng điều đó không giống nhau.
Marcelo Cantos

2
Câu hỏi liên quan về các phần bên trong của StringBuilder stackoverflow.com/q/3564906/38206
Brian Rasmussen

Câu trả lời:


313

Có thể thay đổi và không thay đổi là các từ tiếng Anh có nghĩa là "có thể thay đổi" và "không thể thay đổi" tương ứng. Ý nghĩa của các từ là giống nhau trong bối cảnh CNTT; I E

  • một chuỗi có thể thay đổi có thể được thay đổi và
  • một chuỗi bất biến không thể thay đổi.

Ý nghĩa của những từ này giống nhau trong C # / .NET như trong các ngôn ngữ / môi trường lập trình khác, mặc dù (rõ ràng) tên của các loại có thể khác nhau, cũng như các chi tiết khác.


Đối với hồ sơ:

  • String là loại chuỗi bất biến C # / .Net tiêu chuẩn
  • StringBuilder là loại chuỗi có thể thay đổi C # / .Net tiêu chuẩn

Để "thực hiện thay đổi" trên chuỗi được biểu thị dưới dạng C # String, bạn thực sự tạo một Stringđối tượng mới . Bản gốc Stringkhông thay đổi ... bởi vì nó không thể thay đổi.

Trong hầu hết các trường hợp, tốt hơn là sử dụng Stringvì đó là lý do dễ dàng hơn về chúng; ví dụ: bạn không cần xem xét khả năng một số luồng khác có thể "thay đổi chuỗi của tôi". Tuy nhiên, khi bạn cần xây dựng hoặc sửa đổi một chuỗi bằng cách sử dụng một chuỗi các hoạt động, có thể hiệu quả hơn để sử dụng một StringBuilder.


Và cuối cùng, đối với những người khẳng định rằng a StringBuilderkhông phải là một chuỗi bởi vì nó không phải là bất biến, tài liệu của Microsoft mô tả StringBuildernhư vậy:

"Đại diện cho một chuỗi các ký tự có thể thay đổi . Lớp này không thể được kế thừa."


182

String là bất biến

tức là các chuỗi không thể được thay đổi. Khi bạn thay đổi một chuỗi (bằng cách thêm vào chuỗi chẳng hạn), bạn thực sự đang tạo một chuỗi mới.

Nhưng StringBuilderkhông phải là bất biến (đúng hơn là nó có thể thay đổi)

Vì vậy, nếu bạn phải thay đổi một chuỗi nhiều lần, chẳng hạn như nhiều nối, sau đó sử dụng StringBuilder.


4
Điều gì xảy ra nếu bạn nối chuỗi bất biến nhiều lần? Tải bộ nhớ sử dụng cho chuỗi mà không cần tham chiếu? Hay chỉ chậm thôi?
Rudie

13
@Rudie - cả hai. Mỗi phép nối tạo một đối tượng Chuỗi mới và sao chép tất cả các ký tự của cả hai chuỗi đầu vào. Dẫn đến O(N^2)hành vi ...
Stephen C

@StephenC giải thích với các từ khóa.
Kent Liau

6
nếu bạn phải thay đổi một chuỗi nhiều lần, chẳng hạn như nhiều lần ghép, thì hãy sử dụng StringBuilder ... Điều đó đã xóa sạch tâm trí tôi hoàn toàn cảm ơn ...
katmanco

45

Một đối tượng có thể thay đổi nếu, một khi được tạo, trạng thái của nó có thể được thay đổi bằng cách gọi các hoạt động khác nhau trên nó, nếu không nó là bất biến.

Chuỗi bất biến

Trong C # (và .NET), một chuỗi được đại diện bởi lớp System.String. Các stringtừ khóa là một bí danh cho các lớp học này.

Lớp System.String là bất biến, tức là một khi được tạo, trạng thái của nó không thể thay đổi .

Vì vậy, tất cả các hoạt động bạn thực hiện trên một chuỗi như Substring, Remove, Replace, nối sử dụng '+' điều hành vv sẽ tạo ra một chuỗi mới và gửi lại.

Xem chương trình sau đây để trình diễn -

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

Điều này sẽ in 'chuỗi' và 'bí ẩn' tương ứng.

lợi ích của tính bất biến và tại sao chuỗi là bất biến kiểm tra Tại sao .NET String là bất biến? .

Chuỗi đột biến

Nếu bạn muốn có một chuỗi mà bạn muốn sửa đổi thường xuyên, bạn có thể sử dụng StringBuilderlớp. Các hoạt động trên một StringBuilder thể hiện sẽ sửa đổi cùng một đối tượng .

Để được tư vấn thêm về thời điểm sử dụng, StringBuilder hãy tham khảo Khi nào nên sử dụng StringBuilder? .


Đẹp giải thích!
Hari

36

Tất cả stringcác đối tượng là bất biến trong C #. Các đối tượng của lớp string, một khi được tạo, không bao giờ có thể biểu thị bất kỳ giá trị nào ngoài giá trị mà chúng được tạo. Tất cả các hoạt động dường như "thay đổi" một chuỗi thay vì tạo ra một chuỗi mới. Điều này không hiệu quả với bộ nhớ, nhưng cực kỳ hữu ích liên quan đến việc có thể tin tưởng rằng một chuỗi sẽ không thay đổi hình thức theo bạn - vì miễn là bạn không thay đổi tham chiếu của mình, chuỗi được nhắc đến sẽ không bao giờ thay đổi.

Một đối tượng có thể thay đổi, ngược lại, có các trường dữ liệu có thể được thay đổi. Một hoặc nhiều phương thức của nó sẽ thay đổi nội dung của đối tượng hoặc nó có Thuộc tính mà khi được viết vào sẽ thay đổi giá trị của đối tượng.

Nếu bạn có một đối tượng có thể thay đổi - đối tượng tương tự nhất với Chuỗi là StringBuffer- thì bạn phải tạo một bản sao của nó nếu bạn muốn chắc chắn rằng nó sẽ không thay đổi từ bên dưới bạn. Đây là lý do tại sao các đối tượng có thể thay đổi rất nguy hiểm khi sử dụng làm khóa thành bất kỳ hình thức Dictionaryhoặc tập hợp nào - chính các đối tượng có thể thay đổi và cấu trúc dữ liệu sẽ không có cách nào để biết, dẫn đến dữ liệu bị hỏng, cuối cùng sẽ làm hỏng chương trình của bạn.

Tuy nhiên, bạn có thể thay đổi nội dung của nó - vì vậy, nó hiệu quả hơn nhiều về bộ nhớ so với việc tạo một bản sao hoàn chỉnh vì bạn muốn thay đổi một ký tự hoặc một cái gì đó tương tự.

Nói chung, điều đúng đắn cần làm là sử dụng các đối tượng có thể thay đổi trong khi bạn đang tạo một cái gì đó và các đối tượng bất biến sau khi bạn hoàn thành. Điều này áp dụng cho các đối tượng có hình thức bất biến, tất nhiên; hầu hết các bộ sưu tập không. Tuy nhiên, việc cung cấp các hình thức bộ sưu tập chỉ đọc là tương đối hữu ích, tương đương với bất biến, khi gửi trạng thái bên trong của bộ sưu tập của bạn đến các bối cảnh khác - nếu không, một cái gì đó có thể lấy giá trị trả về đó, làm gì đó cho nó và làm hỏng dữ liệu.


12

Bất biến:

Khi bạn thực hiện một số thao tác trên một đối tượng, nó sẽ tạo ra một đối tượng mới do đó trạng thái không thể sửa đổi như trong trường hợp chuỗi.

Có thể thay đổi

Khi bạn thực hiện một số thao tác trên một đối tượng, chính đối tượng đã sửa đổi không có vật cản mới nào được tạo như trong trường hợp của StringBuilder


8

Trong .NET System.String (còn gọi là chuỗi) là một đối tượng bất biến. Điều đó có nghĩa là khi bạn tạo một đối tượng, bạn không thể thay đổi giá trị của nó sau đó. Bạn chỉ có thể tạo lại một đối tượng bất biến.

System.Text.StringBuilder tương đương với System.String và bạn có thể kiểm tra giá trị của nó

Ví dụ:

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

Tạo MSIL sau: Nếu bạn điều tra mã. Bạn sẽ thấy rằng bất cứ khi nào bạn chọn một đối tượng của System.String bạn thực sự đang tạo một đối tượng mới. Nhưng trong System.Text.StringBuilder bất cứ khi nào bạn thay đổi giá trị của văn bản, bạn không tạo lại đối tượng.

.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
Ummm ... mã được phân tách đó chỉ cho thấy các phép gán con trỏ đang được thực hiện và các phương thức đang được gọi. Nó (hầu như) không có gì để làm với mutable so với bất biến. (Nó thoát cho tôi lý do tại sao một số người nghĩ rằng tháo một số mã ví dụ không thích hợp sẽ giúp mọi người hiểu loại điều Đối với một sự khởi đầu, hầu hết các lập trình viên không thông thạo trong mã CLI..)
Stephen C

6

Các chuỗi có thể thay đổi vì .NET sử dụng chuỗi chuỗi phía sau cảnh. Nó có nghĩa là:

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

Cả name và name2 đều đề cập đến cùng một vị trí bộ nhớ từ nhóm chuỗi. Bây giờ giả sử bạn muốn thay đổi name2 thành:

name2 = "My Loving Country";

Nó sẽ xem xét nhóm chuỗi cho chuỗi "Quốc gia yêu thương của tôi", nếu tìm thấy bạn sẽ nhận được tham chiếu của chuỗi mới thông minh khác "Quốc gia yêu thương của tôi" sẽ được tạo trong nhóm chuỗi và name2 sẽ nhận được tham chiếu về chuỗi đó. Nhưng toàn bộ quá trình " Quốc gia của tôi " không thay đổi vì tên khác như tên vẫn đang sử dụng. Và đó là lý do tại sao chuỗi là NGAY LẬP TỨC .

StringBuilder hoạt động theo cách khác nhau và không sử dụng chuỗi chuỗi. Khi chúng tôi tạo bất kỳ phiên bản nào của StringBuilder:

var address  = new StringBuilder(500);

Nó phân bổ đoạn bộ nhớ có kích thước 500 byte cho trường hợp này và tất cả các hoạt động chỉ sửa đổi vị trí bộ nhớ này và bộ nhớ này không được chia sẻ với bất kỳ đối tượng nào khác. Và đó là lý do tại sao StringBuilderMUTABLE .

Tôi mong nó sẽ có ích.


5

Không, thực sự. Lớp String có thể thay đổi.

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."
}

Kiểu logic này được thực hiện mọi lúc trong các lớp String và StringBuilder. Họ chỉ phân bổ một chuỗi mới mỗi khi bạn gọi Concat, Sub chuỗi, v.v. và sử dụng số học con trỏ để sao chép sang chuỗi mới. Các chuỗi không tự biến đổi, do đó tại sao chúng được coi là "bất biến".


Bằng cách này, đừng không cố gắng này với xâu hoặc bạn sẽ tồi tệ lộn xộn lên chương trình của bạn:

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."

Điều này là do chuỗi ký tự được đặt trong .NET Framework của máy tính để bàn; nói cách khác, barbaztrỏ đến cùng một chuỗi, do đó, một chuỗi sẽ biến đổi chuỗi khác. Đây là tất cả tốt đẹp và mặc dù nếu bạn đang sử dụng một nền tảng không được quản lý như WinRT, mà thiếu thực tập chuỗi.


1
Vâng vâng. Nếu bạn phá vỡ các quy tắc bằng cách bẻ khóa trừu tượng mở được cho là bị đóng, thì hầu như không có gì là bất biến. Bạn có thể thực hiện những điều tương tự trong Java bằng cách sử dụng sự phản chiếu khó chịu ... nhưng JLS nói rằng hành vi bạn nhận được là không xác định.
Stephen C

2

Để làm rõ, không có thứ gọi là chuỗi có thể thay đổi trong C # (hay .NET nói chung). Các langu khác hỗ trợ các chuỗi có thể thay đổi (chuỗi có thể thay đổi) nhưng .NET framework thì không.

Vì vậy, câu trả lời chính xác cho câu hỏi của bạn là TẤT CẢ chuỗi là bất biến trong C #.

chuỗi có một ý nghĩa cụ thể. Từ khóa chữ thường "chuỗi" chỉ là một lối tắt cho một đối tượng được khởi tạo từ lớp System.String. Tất cả các đối tượng được tạo từ lớp chuỗi là LUÔN LUÔN.

Nếu bạn muốn một đại diện văn bản có thể thay đổi thì bạn cần sử dụng một lớp khác như StringBuilder. StringBuilder cho phép bạn lặp đi lặp lại một bộ sưu tập 'từ' và sau đó chuyển đổi nó thành một chuỗi (một lần nữa không thay đổi).


Xin lỗi, nhưng tài liệu của Microsoft StringBuildermâu thuẫn với điều này: xem msdn.microsoft.com/en-us/l Library / từ
Stephen C

1

Giá trị dữ liệu có thể không được thay đổi. Lưu ý: Giá trị biến có thể được thay đổi, nhưng giá trị dữ liệu bất biến ban đầu đã bị loại bỏ và giá trị dữ liệu mới được tạo trong bộ nhớ.


1

trong chi tiết thực hiện.

System.String của CLR2 có thể thay đổi. StringBuilder.Append gọi String.AppendInplace (phương thức riêng tư)

System.String của CLR4 là bất biến. StringBuilder có mảng Char với chunking.


0

Từ http://yassershaikh.com/what-is-the-difference-b between-strings-and-Stringbuilder-in-c-net /

Câu trả lời ngắn: Chuỗi là bất biến - trong khi StringBuilder có thể thay đổi.

Điều đó nghĩa là gì ? Wiki cho biết: Trong hướng đối tượng, một đối tượng bất biến là một đối tượng có trạng thái không thể sửa đổi sau khi được tạo. Điều này trái ngược với một đối tượng có thể thay đổi, có thể được sửa đổi sau khi nó được tạo.

Từ tài liệu Lớp StringBuilder:

Đối tượng String là bất biến. Mỗi khi bạn sử dụng một trong các phương thức trong lớp System.String, bạn sẽ tạo một đối tượng chuỗi mới trong bộ nhớ, yêu cầu phân bổ không gian mới cho đối tượng mới đó.

Trong các tình huống mà bạn cần thực hiện sửa đổi lặp đi lặp lại thành một chuỗi, chi phí liên quan đến việc tạo một đối tượng Chuỗi mới có thể tốn kém.

Lớp System.Text.StringBuilder có thể được sử dụng khi bạn muốn sửa đổi một chuỗi mà không tạo một đối tượng mới. Ví dụ, sử dụng lớp StringBuilder có thể tăng hiệu suất khi nối nhiều chuỗi với nhau trong một vòng lặp.


0

Dưới đây là ví dụ cho trình tạo chuỗi bất biến và trình tạo chuỗi Mutable

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

Sẽ rất tuyệt nếu bạn cũng có thể cung cấp đầu ra @Gokul :)
Roy Lee

Chuỗi con không thay đổi giá trị cơ bản. Nó chỉ trả về một giá trị.
Kye

@Kye và tại sao? bởi vì chuỗi là bất biến :)
LuckyLikey

Mã 2 dòng của bạn -: s.Sub chuỗi (0, 5); Console.WriteLine (s); Ở đây s.subopes () không thay đổi s. ví dụ này không hiển thị nếu chuỗi là bất biến.
Dhananjay

0

Chuỗi trong C # là bất biến. Nếu bạn nối nó với bất kỳ chuỗi nào, bạn thực sự đang tạo một chuỗi mới, đó là đối tượng chuỗi mới! Nhưng StringBuilder tạo ra chuỗi có thể thay đổi.


0

StringBuilder là một tùy chọn tốt hơn để nối chuỗi dữ liệu khổng lồ vì StringBuilder là loại chuỗi có thể thay đổi và đối tượng StringBuilder là loại không thay đổi, điều đó có nghĩa là StringBuilder không bao giờ tạo đối tượng mới trong khi nối chuỗi.

Nếu chúng ta đang sử dụng chuỗi thay vì StringBuilder để đạt được kết nối thì nó sẽ tạo ra thể hiện mới trong bộ nhớ mỗi lần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.