2012-08-17 17 views
11

Trong đoạn mã dưới đây, tôi đang kiểm tra sự bình đẳng của các tham chiếu đối tượng.String Interning

string x = "Some Text"; 
string y = "Some Other Text"; 
string z = "Some Text"; 

Console.WriteLine(object.ReferenceEquals(x, y)); // False 
Console.WriteLine(object.ReferenceEquals(x, z)); // True 
Console.WriteLine(object.ReferenceEquals(y, z)); // False 

y = "Some Text"; 

Console.WriteLine(object.ReferenceEquals(x, y)); // True 
Console.WriteLine(object.ReferenceEquals(x, z)); // True 
Console.WriteLine(object.ReferenceEquals(y, z)); // True 

đây:

  • xz đề cập đến cùng một đối tượng; Tôi có thể nói rằng x được thực tập và z được sử dụng phiên bản taht. Tôi không chắc về điều này; Hãy sửa tôi, nếu tôi sai.
  • Tôi đã thay đổi giá trị y bằng cách gán giá trị giống như x. Tôi nghĩ nó sẽ tạo ra một vật thể mới ở đây; nhưng tôi đã sai, nó sử dụng cùng một tham chiếu.

Câu hỏi của tôi là:

  • Liệu .net sử dụng string interns cho mỗi chuỗi mà tôi sử dụng không?
  • Nếu có, điều đó có gây đau không?
  • Nếu không, cách tham chiếu trở nên giống nhau trong ví dụ trên?
+0

Đọc tốt cho [chuỗi thực tập] (http://blogs.msdn.com/b/ericlippert/archive/2009/09/28/string-interning-and-string-empty.aspx) – V4Vendetta

Trả lời

14

Vâng, biểu thức chuỗi liên tục trong các trình biên dịch được điều trị bằng ldstr, đảm bảo thực tập (thông qua MSDN):

Cơ sở hạ tầng Common Language (CLI) đảm bảo rằng kết quả của hai hướng dẫn ldstr đề cập đến hai siêu dữ liệu các thẻ có cùng chuỗi ký tự trả lại chính xác đối tượng chuỗi giống nhau (một quá trình được gọi là "chuỗi ký tự").

Đây không phải là mỗi chuỗi; nó là biểu thức chuỗi không đổi trong mã của bạn. Ví dụ:

string s = "abc" + "def"; 

chỉ là một biểu thức chuỗi - IL sẽ là một ldstr trên "abcdef" (trình biên dịch có thể tính biểu thức đã soạn).

Điều này không ảnh hưởng đến hiệu suất.

Strings tạo khi chạy không thực tập nội trú tự động, ví dụ:

int i = GetValue(); 
string s = "abc" + i; 

Ở đây, "abc" được thực tập nội trú, nhưng "abc8" thì không. Cũng lưu ý rằng:

char[] chars = {'a','b','c'}; 
string s = new string(chars); 
string t = "abc"; 

lưu ý rằng st là tài liệu tham khảo khác nhau (theo nghĩa đen (giao cho t) được thực tập nội trú, nhưng chuỗi mới (giao cho s) không phải là).

+1

Bạn không có nghĩa là 't' là tập trung, nhưng' s' (chuỗi mới) không phải là? – Kevin

+0

@Kevin Thabks, đã làm rõ –

1

Chuỗi ký tự được tự động interned.

Chuỗi được tạo theo lập trình sẽ không được tập trung theo mặc định (cũng như các chuỗi đầu vào của người dùng).

Ở trên, "Một số văn bản" và "Một số văn bản khác" đã được thực tập và vì bạn đang sử dụng chữ ở những nơi này, bạn sẽ thấy phiên bản thực tập là phiên bản được tham chiếu.

Trong code của bạn, nếu bạn có:

string.Format("{0} {1}", "Some", "Text") 

Bạn sẽ thấy rằng các tài liệu tham khảo trở lại là không giống như cho literals khác.

3

Đỗ es .net sử dụng thực tập chuỗi cho mỗi chuỗi mà tôi sử dụng?

Không, nhưng nó sử dụng nó cho những chuỗi mà nó biết về thời gian biên dịch vì chúng là hằng số trong mã.

string x = "abc"; //interned 
string y = "ab" + "c"; //interned as the same string because the 
         //compiler can work out that it's the same as 
         //y = "abc" at compile time so there's no need 
         //to do that concatenation at run-time. There's 
         //also no need for "ab" or "c" to exist in your 
         //compiled application at all. 
string z = new StreamReader(new FileStream(@"C:\myfile.text")).ReadToEnd(); 
         //z isn't interned because it isn't known at compile 
         //time. Note that @"C:\myfile.text" is interned because 
         //while we don't have a variable we can access it by 
         //it is a string in the code. 

Nếu vậy, không một điều đau khổ thực hiện?

Không, nó giúp thực hiện:

Đầu tiên: Tất cả những chuỗi sẽ được trong bộ nhớ của ứng dụng ở đâu đó. Thực tập có nghĩa là chúng tôi không có bản sao không cần thiết, vì vậy chúng tôi sử dụng ít bộ nhớ hơn. Thứ hai: Nó làm cho so sánh chuỗi chúng ta biết là từ chuỗi nội bộ chỉ siêu nhanh. Thứ ba: Điều đó không tăng lên nhiều, nhưng sự thúc đẩy nó mang lại những so sánh khác. Hãy xem xét mã này tồn tại trong một trong các trình so sánh được tích hợp sẵn:

public override int Compare(string x, string y) 
{ 
    if (object.ReferenceEquals(x, y)) 
    { 
     return 0; 
    } 
    if (x == null) 
    { 
     return -1; 
    } 
    if (y == null) 
    { 
     return 1; 
    } 
    return this._compareInfo.Compare(x, y, this._ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); 
} 

Đây là thứ tự, nhưng áp dụng cho kiểm tra bình đẳng/bất bình đẳng. Để kiểm tra hai chuỗi bằng hoặc đặt chúng theo thứ tự yêu cầu chúng ta thực hiện thao tác O (n) trong đó n là tỷ lệ thuận với độ dài của chuỗi (ngay cả trong trường hợp một số bỏ qua và thông minh có thể được thực hiện, nó vẫn tỷ lệ thuận) . Điều này có khả năng làm chậm chuỗi dài và so sánh chuỗi là thứ mà rất nhiều ứng dụng thực hiện rất nhiều thời gian - một nơi tuyệt vời để tăng tốc. Nó cũng chậm nhất cho trường hợp bình đẳng (vì thời điểm chúng ta tìm thấy một sự khác biệt chúng ta có thể trả về một giá trị, nhưng các chuỗi bằng nhau phải được kiểm tra hoàn toàn). Tất cả mọi thứ luôn luôn bằng với ngay cả khi bạn xác định lại những gì "bằng" có nghĩa là (văn bản nhạy cảm, không nhạy cảm, văn hóa khác nhau - mọi thứ vẫn bằng với chính nó và nếu bạn tạo ghi đè Equals() không tuân theo điều đó, bạn sẽ có lỗi). Tất cả mọi thứ luôn luôn được đặt hàng tại cùng một điểm như một cái gì đó bằng.Điều này có nghĩa là hai điều:

  1. Chúng tôi luôn có thể cân nhắc điều gì đó tương đương với chính nó mà không thực hiện thêm bất kỳ công việc nào.
  2. Chúng tôi luôn có thể cung cấp giá trị so sánh của 0 để so sánh nội dung nào đó với chính nó mà không cần thêm bất kỳ công việc nào.

Do đó, mã ở trên cắt ngắn trong trường hợp này mà không phải thực hiện so sánh phức tạp và tốn kém hơn. Ngoài ra còn có không có từ phía xuống kể từ khi chúng tôi đã không bao gồm trường hợp này chúng tôi sẽ phải thêm vào một thử nghiệm cho trường hợp mà cả hai giá trị thông qua nơi null anyway.

Bây giờ, điều đó xảy ra khi so sánh một cái gì đó với chính nó xuất hiện khá tự nhiên với cách một số thuật toán hoạt động, vì vậy nó luôn có giá trị thực hiện. Tuy nhiên, chuỗi ký tự tăng thời gian khi hai chuỗi chúng ta có trong các giá trị khác nhau (ví dụ: xz ở đầu câu hỏi của bạn), vì vậy nó tăng tần suất cắt ngắn hoạt động cho chúng tôi.

Đó là một tối ưu hóa nhỏ trong hầu hết thời gian, nhưng chúng tôi nhận được nó miễn phí và chúng tôi nhận được nó thường xuyên như vậy là tuyệt vời để có nó. Thực tế takeaway từ này - nếu bạn đang viết một Equals hoặc một Compare xem xét liệu bạn cũng nên sử dụng cắt ngắn này.

Câu hỏi liên quan sau đó là "tôi có nên thực tập mọi thứ không?"

Ở đây, chúng tôi phải xem xét nhược điểm mà các chuỗi được biên dịch không có. Interning không bao giờ lãng phí với biên dịch trong chuỗi, bởi vì họ phải ở đâu đó. Tuy nhiên, nếu bạn đọc một chuỗi từ một tập tin, tập trung vào nó, và sau đó không bao giờ sử dụng nó một lần nữa nó sẽ sống một thời gian dài, và đó là lãng phí. Nếu bạn đã làm nó tất cả các thời gian, bạn có thể làm tê liệt sử dụng bộ nhớ của bạn.

Hãy tưởng tượng mặc dù bạn thường xuyên đọc nhiều mục bao gồm một số số nhận dạng. Bạn thường xuyên sử dụng các số nhận dạng này để đối sánh các mục với dữ liệu từ một nguồn khác. Có một bộ định danh nhỏ sẽ từng thấy (chỉ có vài trăm giá trị có thể). Sau đó, bởi vì kiểm tra bình đẳng là những gì các chuỗi này là tất cả về, và không có nhiều người trong số họ, interning (trên cả dữ liệu đọc và dữ liệu bạn so sánh nó với - nó vô nghĩa nếu không) trở thành một chiến thắng. Hoặc, giả sử rằng có một vài nghìn đối tượng như vậy và dữ liệu chúng tôi khớp với nó luôn được lưu trong bộ nhớ - điều đó có nghĩa là các chuỗi đó luôn ở đâu đó trong bộ nhớ, vì vậy việc thực tập trở thành không có trí tuệ thắng lợi. (Trừ khi có khả năng rất nhiều kết quả "không tìm thấy" - thực tập những định danh đó chỉ để không tìm thấy kết quả trùng khớp là mất).

Cuối cùng, cùng một kỹ thuật cơ bản có thể được thực hiện khác nhau. XmlReader ví dụ các chuỗi cửa hàng mà nó so sánh trong một số NameTable hoạt động như một hồ bơi thực tập riêng, nhưng toàn bộ điều có thể được thu thập khi hoàn tất. Bạn cũng có thể áp dụng kỹ thuật này cho bất kỳ loại tham chiếu nào sẽ không bị thay đổi trong thời gian được gộp lại (cách tốt nhất để đảm bảo rằng nó không thay đổi được vì vậy nó sẽ không thay đổi trong bất kỳ thời điểm nào). Sử dụng kỹ thuật này với các bộ sưu tập rất lớn với số lượng bản sao lớn có thể giảm thiểu sử dụng bộ nhớ (tiết kiệm lớn nhất của tôi là ít nhất 16GB - có thể nhiều hơn nhưng máy chủ vẫn bị treo vào thời điểm đó trước khi kỹ thuật được áp dụng) và/hoặc tốc độ lên so sánh.

Các vấn đề liên quan