2011-01-21 13 views
6
A = string.Concat("abc","def") 

B = "abc" + "def" 

Một vs BNgoài ra các chuỗi trong C#, làm thế nào trình biên dịch hiện nó?

Gần đây tôi đã bối rối vì sao nhiều người sẽ nói rằng chắc chắn Một hiện một xử lý nhanh hơn nhiều so với B. Tuy nhiên, vấn đề là họ sẽ chỉ nói vì ai đó nói như vậy hay bởi vì nó chỉ là con đường của nó. Tôi cho rằng tôi có thể nghe một lời giải thích tốt hơn nhiều từ đây.

Trình biên dịch xử lý các chuỗi này như thế nào?

Cảm ơn bạn!

+2

Với các chuỗi có kích thước này, nó sẽ không quan trọng –

Trả lời

12

Điều đầu tiên tôi làm khi tham gia nhóm biên dịch C# là tôi đã viết lại trình tối ưu hóa cho các chuỗi nối. Thời gian tốt.

Như đã lưu ý, chuỗi concats của chuỗi không đổi được thực hiện tại thời gian biên dịch. dây không liên tục làm một số công cụ ưa thích:

a + b --> String.Concat(a, b) 
a + b + c --> String.Concat(a, b, c) 
a + b + c + d --> String.Concat(a, b, c, d) 
a + b + c + d + e --> String.Concat(new String[] { a, b, c, d, e }) 

Những lợi ích của những tối ưu hóa là rằng phương pháp String.Concat có thể xem xét tất cả các đối số, xác định tổng độ dài của họ, và sau đó thực hiện một chuỗi lớn mà có thể giữ tất cả các kết quả.

Đây là một điều thú vị.Giả sử bạn có phương thức M trả về một chuỗi:

s = M() + ""; 

Nếu M() trả về null thì kết quả là chuỗi rỗng. (null + trống rỗng.) Nếu M không trả về null thì kết quả sẽ không thay đổi bằng cách nối chuỗi rỗng. Vì vậy, điều này thực sự được tối ưu hóa như không phải là một cuộc gọi đến String.Concat ở tất cả! Nó trở thành

s = M() ?? "" 

Gọn gàng, eh?

5

Trong C#, toán tử bổ sung cho chuỗi chỉ là cú pháp đường cho String.Concat. Bạn có thể xác minh rằng bằng cách mở cụm đầu ra trong phản xạ.

Một điều cần lưu ý là, nếu bạn có chuỗi ký tự (hoặc hằng số) trong mã của bạn, chẳng hạn như trong ví dụ, trình biên dịch thậm chí còn thay đổi điều này thành B = "abcdef".

Nhưng, nếu bạn sử dụng String.Concat với hai chuỗi ký tự hoặc hằng số, String.Concat sẽ vẫn được gọi, bỏ qua tối ưu hóa và do đó hoạt động + thực sự sẽ nhanh hơn.

Vì vậy, để tóm tắt:

stringA + stringB trở thành String.Concat(stringA, stringB).
"abc" + "def" trở thành "abcdef "
String.Concat("abc", "def") giữ nguyên

Cái gì khác tôi chỉ cần phải thử:

Trong C++/CLI, "abc" + "def" + "ghi" thực sự là dịch sang String.Concat(String.Concat("abc", "def"), "ghi")

+3

Không có hai chuỗi ký tự, nó không: 'B' sẽ chỉ được đặt trực tiếp thành" abcdef ". – LukeH

+0

đã được thêm ngay lập tức sau khi đăng :) – Botz3000

1

Trên thực tế, B được giải quyết trong thời gian biên dịch. Bạn sẽ kết thúc với B = "abcdef" trong khi đối với A, nối được hoãn cho đến thời gian thực hiện.

+1

Để thêm vào điều này, Việc sử dụng '+' trên chuỗi khi * không * đối đầu với chữ sẽ được chuyển thành một cuộc gọi duy nhất thành 'string.Concat()' – Joey

5
+1

+1 ! thanks – naveen

+0

Chỉ cần một lưu ý: Tôi nghĩ rằng nhiều người nhấn mạnh StringBuilder quá nhiều. Ngoài ra còn có lớp StringWriter trong .NET dễ sử dụng hơn nhiều, vì giao diện công khai của nó rất giống với những gì mọi người biết từ lớp Console. –

+0

Theo MSDN, 'StringWriter' là một trình bao bọc xung quanh' StringBuilder'. Vì vậy, nó không phải là quan trọng để đề cập đến 'StringWriter' trong bối cảnh tối ưu hóa mã nếu' StringBuilder' đã được thảo luận. – Brian

1

Nếu chuỗi là chữ, như trong câu hỏi của bạn, sau đó nối của các dây giao cho B sẽ được thực hiện tại thời gian biên dịch. Ví dụ bạn chuyển đến:

string a = string.Concat("abc", "def"); 
string b = "abcdef"; 

Nếu chuỗi không literals sau đó trình biên dịch sẽ dịch các nhà điều hành + thành một cuộc gọi Concat.

Vì vậy, đây ...

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = x + y; 

... được phiên dịch sang này tại thời gian biên dịch:

string x = GetStringFromSomewhere(); 
string y = GetAnotherString(); 

string a = string.Concat(x, y); 
string b = string.Concat(x, y); 
1

Trong trường hợp đặc biệt này, hai đang thực sự giống hệt nhau. Trình biên dịch sẽ biến đổi biến thể thứ hai, một biến thể sử dụng toán tử +, thành một cuộc gọi đến Concat, phiên bản đầu tiên.

Vâng, có nghĩa là, nếu hai thực sự chứa các biến chuỗi được nối.

Mã này:

B = "abc" + "def"; 

thực sự biến thành này, mà không cần nối ở tất cả:

B = "abcdef"; 

Điều này có thể được thực hiện bởi vì kết quả của việc bổ sung có thể được tính tại thời gian biên dịch, vì vậy trình biên dịch thực hiện điều này.

Tuy nhiên, nếu bạn đã sử dụng một cái gì đó như thế này:

A = String.Concat(stringVariable1, stringVariable2); 
B = stringVariable1 + stringVariable2; 

Sau đó hai sẽ tạo ra cùng mã.

Tuy nhiên, tôi muốn biết chính xác những gì "nhiều" đó đã nói, vì tôi nghĩ đó là điều gì đó khác biệt.

Những gì tôi nghĩ rằng họ nói là chuỗi nối là xấu, và bạn nên sử dụng StringBuilder hoặc tương tự.

Ví dụ, nếu bạn làm điều này:

String s = "test"; 
for (int index = 1; index <= 10000; index++) 
    s = s + "test"; 

Sau đó, những gì xảy ra ở đây là mỗi lần lặp qua các vòng lặp, bạn sẽ xây dựng một chuỗi mới, và thẫn thờ nhìn người già được hưởng thu gom rác thải.

Ngoài ra, mỗi chuỗi mới như vậy sẽ có tất cả nội dung của tệp cũ được sao chép vào nó, điều đó có nghĩa là bạn sẽ di chuyển một lượng lớn bộ nhớ xung quanh.

Trong khi đoạn mã sau:

StringBuilder sb = new StringBuilder("test"); 
for (int index = 1; index <= 10000; index++) 
    sb.Append("test"); 

thay vào đó sẽ sử dụng một bộ đệm bên trong, đó là lớn hơn so với những gì cần được, chỉ trong trường hợp bạn cần phải thêm nhiều văn bản hơn vào nó. Khi bộ đệm đó đầy, một cái mới lớn hơn sẽ được phân bổ, và cái cũ còn lại để thu gom rác.

Vì vậy, về mặt sử dụng bộ nhớ và mức sử dụng CPU, biến thể sau này tốt hơn nhiều.

Ngoài ra, tôi sẽ cố gắng tránh tập trung quá nhiều vào "là biến thể mã X tốt hơn Y", ngoài những gì bạn đã có trải nghiệm. Ví dụ, tôi sử dụng StringBuilder bây giờ chỉ vì tôi nhận thức được trường hợp, nhưng đó không phải là để nói rằng tất cả các mã tôi viết mà sử dụng nó thực sự cần nó.

Cố gắng tránh dành thời gian vi tối ưu hóa mã của bạn, cho đến khi bạn biết mình bị nút cổ chai. Vào thời điểm đó, mẹo thông thường về biện pháp đầu tiên, được cắt giảm sau đó, vẫn có hiệu lực.