Để bắt đầu, tôi đồng ý rằng các câu lệnh goto phần lớn không liên quan đến cấu trúc cấp cao hơn trong ngôn ngữ lập trình hiện đại và không được sử dụng khi thay thế phù hợp có sẵn.Các cách khác để giải quyết "khởi tạo vòng lặp" trong C#
Tôi đã đọc lại phiên bản gốc của Bộ luật Steve McConnell hoàn thành gần đây và đã quên mất đề xuất của mình về một vấn đề mã hóa phổ biến. Tôi đã đọc nó nhiều năm trước đây khi tôi mới bắt đầu và không nghĩ rằng tôi nhận ra công thức nấu ăn sẽ hữu ích như thế nào. Vấn đề mã hóa là như sau: khi thực hiện một vòng lặp, bạn thường cần thực hiện một phần của vòng lặp để khởi tạo trạng thái và sau đó thực hiện vòng lặp với một số logic khác và kết thúc mỗi vòng lặp với cùng một logic khởi tạo. Một ví dụ cụ thể là thực hiện phương thức String.Join (delimiter, array).
Tôi nghĩ rằng mọi thứ đầu tiên là về vấn đề này. Giả sử phương thức chắp thêm được định nghĩa để thêm đối số vào giá trị trả lại của bạn.
bool isFirst = true;
foreach (var element in array)
{
if (!isFirst)
{
append(delimiter);
}
else
{
isFirst = false;
}
append(element);
}
Lưu ý: Tối ưu hóa một chút là loại bỏ và đặt nó ở cuối vòng lặp. Một nhiệm vụ thường là một lệnh đơn và tương đương với một lệnh khác và giảm số khối cơ bản xuống 1 và tăng kích thước khối cơ bản của phần chính. Kết quả là thực hiện một điều kiện trong mỗi vòng lặp để xác định xem bạn có nên thêm dấu phân tách hay không.
Tôi cũng đã xem và sử dụng các tính năng khác để giải quyết vấn đề vòng lặp chung này. Bạn có thể thực thi mã phần tử ban đầu đầu tiên bên ngoài vòng lặp, sau đó thực hiện vòng lặp của bạn từ phần tử thứ hai đến cuối. Bạn cũng có thể thay đổi logic để luôn nối thêm phần tử sau đó dấu tách và khi vòng lặp hoàn tất, bạn có thể chỉ cần xóa dấu phân cách cuối cùng mà bạn đã thêm vào.
Giải pháp thứ hai có xu hướng là giải pháp mà tôi chỉ thích vì nó không trùng lặp với bất kỳ mã nào. Nếu logic của trình tự khởi tạo thay đổi, bạn không phải nhớ sửa nó ở hai nơi. Tuy nhiên nó đòi hỏi thêm "công việc" để làm một cái gì đó và sau đó hoàn tác nó, gây ra ít nhất thêm chu kỳ CPU và trong nhiều trường hợp như ví dụ String.Join của chúng tôi yêu cầu thêm bộ nhớ là tốt.
Tôi vô cùng phấn khích sau đó để đọc xây dựng
var enumerator = array.GetEnumerator();
if (enumerator.MoveNext())
{
goto start;
do {
append(delimiter);
start:
append(enumerator.Current);
} while (enumerator.MoveNext());
}
Lợi ích ở đây là bạn không nhận được mã trùng lặp và bạn không nhận được công việc bổ sung. Bạn bắt đầu vòng lặp một nửa của bạn vào việc thực hiện vòng lặp đầu tiên của bạn và đó là khởi tạo của bạn. Bạn bị giới hạn để mô phỏng các vòng lặp khác với cấu trúc trong khi thực hiện nhưng bản dịch dễ dàng và việc đọc nó không khó.
Vì vậy, bây giờ là câu hỏi. Tôi hạnh phúc đã đi thử thêm vào một số mã tôi đã làm việc trên và thấy nó không hoạt động. Hoạt động tốt trong C, C++, Cơ bản nhưng hóa ra trong C# bạn không thể nhảy đến một nhãn bên trong một phạm vi từ vựng khác mà không phải là phạm vi gốc. Tôi đã rất thất vọng. Vì vậy, tôi đã tự hỏi, cách tốt nhất để đối phó với vấn đề mã hóa rất phổ biến này là gì (tôi thấy nó chủ yếu trong thế hệ dây) trong C#?
Để có lẽ cụ thể hơn với các yêu cầu:
- Đừng lặp lại đang
- Đừng làm việc không cần thiết
- Đừng chậm hơn 2 hoặc 3 lần so với các mã khác
- Hãy đọc
tôi nghĩ khả năng đọc là điều duy nhất mà cho là có thể chịu đau khổ với công thức tôi đã nêu. Tuy nhiên nó không hoạt động trong C# vậy điều tốt nhất tiếp theo là gì?
* Chỉnh sửa * Tôi đã thay đổi tiêu chí hiệu suất của mình vì một số cuộc thảo luận. Hiệu suất nói chung không phải là một yếu tố hạn chế ở đây, vì vậy mục tiêu chính xác hơn nên được để không được bất hợp lý, không phải là nhanh nhất bao giờ hết.
Lý do tôi không thích triển khai thay thế mà tôi đề xuất là vì chúng trùng lặp mã rời khỏi phòng để thay đổi một phần và không phải phần kia hoặc đối với trường hợp tôi thường chọn yêu cầu "hoàn tác" thao tác. để hoàn tác điều bạn vừa làm. Với thao tác chuỗi cụ thể, điều này thường khiến bạn mở cho một lỗi hoặc không tính đến một mảng trống và cố gắng hoàn tác một cái gì đó không xảy ra.
Nhưng một khi bạn biết String.Join, bao lâu thì bạn thấy mình thực sự viết vòng lặp như vậy nữa? –
Tôi tìm thấy tất cả các loại lý do không sử dụng nó. Hiện tại tôi đang làm việc trên một thứ gì đó thực hiện rất nhiều thao tác chuỗi với một cây đối tượng. Tôi phải vượt qua một nhà xây dựng chuỗi xung quanh để không có nhiều phân bổ không cần thiết khi tôi dịch một biểu đồ cây tùy ý thành một chuỗi. Sử dụng quá tải này là không thể bởi vì nó không tồn tại cho stringbuilder, và thậm chí nếu nó đã làm bạn không thể sử dụng một cái gì đó như sb.Join ("delim", Children.Select (child => child.Build (sb)) –
@Peter Oehlert: Bạn có thể viết một hàm trả về IEnumerable, sau đó sử dụng trả về lợi nhuận để trả lại kết quả của bạn theo bất cứ thứ tự nào bạn muốn. –