2009-04-22 23 views
5

Câu hỏi của tôi là: Chuỗi nối trong C# có an toàn không? Nếu chuỗi nối dẫn đến các lỗi không mong muốn và thay thế chuỗi nối đó bằng cách sử dụng StringBuilder sẽ làm cho các lỗi đó biến mất, điều đó có thể chỉ ra điều gì?Chuỗi Kết nối không an toàn trong C#, cần sử dụng StringBuilder?

Bối cảnh: Tôi đang phát triển một dòng lệnh nhỏ C# ứng dụng. Nó lấy các đối số dòng lệnh, thực hiện một truy vấn SQL hơi phức tạp và đầu ra khoảng 1300 hàng dữ liệu vào một tệp XML được định dạng.

Chương trình ban đầu của tôi sẽ luôn chạy tốt ở chế độ gỡ lỗi. Tuy nhiên, trong chế độ phát hành nó sẽ nhận được khoảng kết quả SQL 750, và sau đó chết với một lỗi. Lỗi là một cột dữ liệu nào đó không thể đọc được, thậm chí thông qua phương thức Read() của đối tượng SqlDataReader vừa trả về true.

Sự cố này đã được khắc phục bằng cách sử dụng StringBuilder cho tất cả các hoạt động trong mã, trước đây đã có "string1 + string2". Tôi không nói về chuỗi nối trong vòng lặp truy vấn SQL, nơi StringBuilder đã được sử dụng. Tôi đang nói về các kết nối đơn giản giữa hai hoặc ba biến chuỗi ngắn trước đó trong mã.

Tôi có ấn tượng rằng C# đủ thông minh để xử lý việc quản lý bộ nhớ để thêm một vài chuỗi với nhau. Liệu tôi có sai? Hoặc điều này cho thấy một số loại vấn đề mã khác?

+0

Bạn có thể dán vào đoạn mã bằng chuỗi nối với phiên bản StringBuilder không? Lý do duy nhất tôi có thể nghĩ rằng bạn có thể nhận được một sự khác biệt với chuỗi nối với StringBuilder là trong đó quá tải của một số điều được gọi là, nhưng điều đó không nên có hiệu lực như bạn đang mô tả. –

+0

Mã gốc: chuỗi filepath = đường dẫn + fileroot + ".xml"; Mã được cập nhật: chuỗi filepath = new StringBuilder (đường dẫn) . Áp dụng (fileroot) .Append (". Xml"). ToString(); Tôi đã thực hiện các thay đổi tương tự ở một số nơi. Đây là tất cả trước khi vòng lặp chính. Bên trong vòng lặp chính, tôi đã luôn sử dụng StringBuilder để xây dựng nội dung tệp XML. (Tôi không sử dụng các API XML vì nó nhanh hơn và điều này được cho là chỉ là một chương trình nhanh chóng và dơ bẩn.) –

Trả lời

7

Ngoài những gì bạn đang làm có thể được thực hiện tốt nhất với các API XML thay vì chuỗi hoặc StringBuilder, tôi nghi ngờ rằng lỗi bạn thấy là do nối chuỗi. Có lẽ chuyển sang StringBuilder chỉ che giấu lỗi hoặc đi qua nó một cách duyên dáng, nhưng tôi nghi ngờ bằng cách sử dụng dây thực sự là nguyên nhân.

+0

Cảm ơn tất cả mọi người cho câu trả lời của bạn. Tôi vẫn thấy lỗi cho một số tập dữ liệu nhất định sau tất cả các thay đổi của tôi. Tôi nghĩ rằng gốc thực sự của vấn đề là trong kết nối SQL, và rằng "chuyển sang StringBuilder chỉ che giấu lỗi" như Johannes nói. Tôi đã khắc phục sự cố bằng cách sử dụng lớp trình bao bọc SQL từ một dự án khác. Lớp này chuyển đổi toàn bộ kết quả SQL thành một đối tượng Dictionary, do đó không cần phải giữ kết quả và kết nối SQL mở. –

11

Nối chuỗi là an toàn mặc dù nhiều bộ nhớ hơn so với sử dụng một StringBuilder nếu liên hệ với số lượng lớn các chuỗi trong một vòng lặp. Và trong trường hợp cực đoan bạn có thể hết bộ nhớ.

Nó gần như chắc chắn là một lỗi trong mã của bạn.

Có thể bạn đang liên kết một số lượng lớn các chuỗi. Hoặc có thể đó là một cái gì đó hoàn toàn khác.

Tôi sẽ quay trở lại gỡ lỗi mà không có bất kỳ định kiến ​​nào về nguyên nhân gốc - nếu bạn vẫn gặp sự cố, hãy cố gắng giảm thiểu số lượng tối thiểu cần thiết để trả lại sự cố và đăng mã.

-3

Khi gộp chuỗi lại với nhau, tôi luôn sử dụng StringBuilder. Nó được thiết kế cho nó và hiệu quả hơn mà chỉ đơn giản là sử dụng "string1 + string2".

+3

Lời khuyên không tốt. string1 + string2 nhanh hơn StringBuilder trong nhiều tình huống: StringBuilder thắng khi thực hiện một số lượng lớn các kết nối trong một vòng lặp. – Joe

+0

Cảm ơn bạn đã cho tôi biết. Tôi đã đọc rằng nó đã nhanh hơn trong "C# thông qua CLR", nhưng nếu đó không phải là trường hợp, nó là tốt để biết. – patjbs

+0

Tôi vừa chạy một số bài kiểm tra và thậm chí cả hai thao tác hai chuỗi ngắn thông qua "str1 + str2" IS thực sự chậm hơn, hoặc ở mức tốt nhất không nhanh hơn, hơn là sử dụng StringBuilder. Và một số bài viết blog tuyệt vời tôi đã đọc có xu hướng đồng ý với kết luận đó. Trong khi sử dụng str1 + str2 có thể là một thuận tiện tiện dụng và không phải là một tác động đáng kể hiệu suất trên concatanation chuỗi nhỏ, StringBuilder chắc chắn là không tồi tệ hơn, và sẽ giành chiến thắng như số lượng concats phát triển (ít nhất là 4 sự khác biệt là sig) – patjbs

3

Phiên bản ghép nối với phiên bản trình tạo chuỗi mất bao lâu? Có thể kết nối của bạn với DB đang bị đóng. Nếu bạn đang làm rất nhiều nối, tôi sẽ đi w/StringBuilder vì nó là một chút hiệu quả hơn.

1

Một nguyên nhân có thể là các chuỗi không thay đổi trong .Net, do đó khi bạn thực hiện thao tác trên một chuỗi, chẳng hạn như nối bạn thực sự đang tạo một chuỗi mới.

Một nguyên nhân khác có thể là độ dài chuỗi là một int sao cho độ dài tối đa có thể là Int32.MaxValue hoặc 2,147,483,647.

Trong cả hai trường hợp, StringBuilder tốt hơn "string1 + string2" cho loại thao tác này. Mặc dù, việc sử dụng các khả năng XML tích hợp sẽ còn tốt hơn nữa.

0

Đây là ảnh của tôi trong bóng tối ...

Chuỗi trong .NET (không phải là chuỗi xây dựng) đi vào Chuỗi Intern Pool. Điều này về cơ bản là một khu vực được CLR quản lý để chia sẻ các chuỗi để cải thiện hiệu suất. Phải có một số giới hạn ở đây, mặc dù tôi không biết giới hạn đó là bao nhiêu. Tôi tưởng tượng tất cả các nối bạn đang làm là nhấn trần của chuỗi intern intern. Vì vậy, SQL nói có Tôi có một giá trị cho bạn, nhưng nó không thể đặt nó ở bất cứ đâu để bạn có được một ngoại lệ.

Kiểm tra nhanh chóng và dễ dàng sẽ là nGen lắp ráp của bạn và xem bạn có gặp lỗi hay không. Sau khi nGen'ing, bạn sẽ không còn sử dụng hồ bơi nữa.

Nếu không thành công, tôi sẽ liên hệ với Microsoft để thử và nhận một số chi tiết khó. Tôi nghĩ ý tưởng của tôi nghe có vẻ hợp lý, nhưng tôi không biết tại sao nó lại hoạt động ở chế độ gỡ lỗi. Có lẽ trong chuỗi chế độ gỡ lỗi không được tập trung. Tôi cũng không có chuyên gia.

13

Để trả lời câu hỏi của bạn: Chuỗi contatenation trong C# (và .NET nói chung) "an toàn", nhưng thực hiện nó trong một vòng lặp chặt chẽ như bạn mô tả là có khả năng gây áp lực bộ nhớ nghiêm trọng và đặt căng thẳng trên bộ thu gom rác.

Tôi sẽ nguy hiểm khi đoán rằng các lỗi bạn nói có liên quan đến cạn kiệt nguồn lực, nhưng sẽ hữu ích nếu bạn có thể cung cấp thêm chi tiết - ví dụ, bạn có nhận được ngoại lệ không? Ứng dụng có chấm dứt bất thường không?

Bối cảnh: NET dây là không thay đổi, vì vậy khi bạn làm một nối như thế này:

var stringList = new List<string> {"aaa", "bbb", "ccc", "ddd", //... }; 
string result = String.Empty; 
foreach (var s in stringList) 
{ 
    result = result + s; 
} 

Đây là tương đương như sau:

string result = ""; 
result = "aaa" 
string temp1 = result + "bbb"; 
result = temp1; 
string temp2 = temp1 + "ccc"; 
result = temp2; 
string temp3 = temp2 + "ddd"; 
result = temp3; 
// ... 
result = tempN + x; 

Mục đích của việc này Ví dụ là để nhấn mạnh rằng mỗi lần xung quanh vòng lặp kết quả trong việc phân bổ một chuỗi tạm thời mới.

Vì các chuỗi không thay đổi, thời gian chạy không có tùy chọn thay thế nhưng để cấp phát một chuỗi mới mỗi khi bạn thêm chuỗi khác vào cuối kết quả của mình.

Mặc dù chuỗi result được cập nhật liên tục để trỏ đến kết quả trung gian mới nhất và lớn nhất, bạn đang tạo ra rất nhiều chuỗi tạm thời chưa được đặt tên này đủ điều kiện thu gom rác ngay lập tức.

Khi kết thúc chuỗi này, bạn sẽ có các chuỗi sau được lưu trữ trong bộ nhớ (giả sử, để đơn giản, bộ thu gom rác chưa chạy).

string a = "aaa"; 
string b = "bbb"; 
string c = "ccc"; 
// ... 
string temp1 = "aaabbb"; 
string temp2 = "aaabbbccc"; 
string temp3 = "aaabbbcccddd"; 
string temp4 = "aaabbbcccdddeee"; 
string temp5 = "aaabbbcccdddeeefff"; 
string temp6 = "aaabbbcccdddeeefffggg"; 
// ... 

Mặc dù tất cả các biến tạm thời tiềm ẩn này đủ điều kiện thu gom rác gần như ngay lập tức, chúng vẫn phải được phân bổ. Khi thực hiện nối trong một vòng lặp chặt chẽ này sẽ đặt rất nhiều căng thẳng vào bộ thu gom rác và, nếu không có gì khác, sẽ làm cho mã của bạn chạy rất chậm. Tôi đã thấy tác động hiệu suất của bàn tay đầu tiên này, và nó trở nên thực sự ấn tượng khi chuỗi nối của bạn trở nên lớn hơn.

Cách tiếp cận được khuyến nghị là luôn sử dụng StringBuilder nếu bạn đang thực hiện nhiều hơn một vài chuỗi nối.StringBuilder sử dụng bộ đệm có thể thay đổi để giảm số lần phân bổ cần thiết để tạo chuỗi của bạn.

+0

Câu trả lời tuyệt vời :) – patjbs

+4

Tôi khá chắc chắn một + b + c + d ... trong một câu lệnh không tạo ra các chuỗi trung gian. Nó hoạt động giống như String.Concat. – Joe

+0

Joe: Bạn nói đúng. Tôi đã đi quá xa trong việc đơn giản hóa câu trả lời. Tôi đã thêm một vòng lặp để làm cho nó thực tế hơn ngay bây giờ. Điều đó có tốt hơn không? –

0

string.Concat(string[]) là cách nhanh nhất để nối chuỗi. Nó tiêu hao hiệu quả StringBuilder về hiệu suất khi được sử dụng trong các vòng lặp, đặc biệt nếu bạn tạo StringBuilder trong mỗi lần lặp. Có vô số tài liệu tham khảo nếu bạn "Định dạng chuỗi C# so với trình xây dựng chuỗi" của Google hoặc tương tự như vậy. http://www.codeproject.com/KB/cs/StringBuilder_vs_String.aspx cung cấp cho bạn một ideer về thời gian. Ở đây string.Join thắng thử nghiệm ghép nối nhưng tôi tin rằng điều này là do string.Concat(string, string) được sử dụng thay vì phiên bản quá tải phải mất một mảng. Nếu bạn nhìn vào mã MSIL được tạo ra bởi các phương pháp khác nhau, bạn sẽ thấy những gì đang diễn ra bên dưới mui xe.

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