Có một quy ước đặt tên dứt khoát cho các phương thức trả về IAsyncEnumerable không?


10

Sau khi C # 5 giới thiệu asyncawaitmô hình cho lập trình không đồng bộ, cộng đồng C # đã đến một quy ước đặt tên để thêm hậu tố "Async" cho các phương thức trả về loại được chờ đợi, như sau:

interface Foo
{
   Task BarAsync();
}

Nhiều máy phân tích mã tĩnh (cả dựa trên Roslyn và không dựa trên Roslyn) đã được viết để phụ thuộc vào quy ước đặt tên này khi phát hiện mùi mã xung quanh lập trình không đồng bộ.

Bây giờ, C # 8 đã đưa ra khái niệm về liệt kê không đồng bộ, bản thân chúng không được chờ đợi nhưng có thể được sử dụng cùng với await foreach, dường như có hai tùy chọn để trả về các phương thức đặt tên IAsyncEnumerable:

interface Foo
{
    // No "Async" suffix, indicating that the return value is not awaitable.
    IAsyncEnumerable<T> Bar<T>();
}

hoặc là

interface Foo
{
    // With "Async" suffix, indicating that the overall logic is asynchronous.
    IAsyncEnumerable<T> BarAsync<T>();
}

Đã có một hướng dẫn quy ước đặt tên dứt khoát (từ nhóm ngôn ngữ C #, .NET Foundation hoặc các cơ quan khác) về các lựa chọn ở trên, như cách quy ước đặt tên C # 5 được chuẩn hóa rõ ràng và không để lại sự phán xét dựa trên ý kiến ​​của các lập trình viên?


2
Tùy chọn 2 nghe có vẻ đúng ở đây, vì bạn chạy mã này không đồng bộ (phương thức đánh dấu asyncvà sử dụng awaitbên trong) và mẫu
msDN

1
Để trả lời cho việc đóng câu hỏi, tôi đã chỉnh sửa câu hỏi để hỏi liệu có tồn tại một quy ước đặt tên dứt khoát hay không và để đưa ra ví dụ về cách một quy ước đặt tên dứt khoát trong C # 5 dẫn đến sự phát triển trong phân tích mã tĩnh. Do đó, việc có C # 8 theo mô hình đó sẽ dẫn đến các cải tiến chất lượng mã có thể đo lường được ngoài các ưu tiên mã hóa dựa trên quan điểm.
hwaien

Bản thân .NET Core sử dụng Asynchậu tố cho các phương thức trả về IAsyncEnumerable, ví dụ ChannelReader.ReadAllAsync . Trong các trường hợp khác, ví dụ như trong EF Core, AsAsyncEnumerable()được sử dụng đã rõ ràng
Panagiotis Kanavos

Hướng dẫn đặt tên tồn tại để giúp con người dễ hiểu mã hơn. Nếu mục đích của mã không rõ ràng, hãy thêm hậu tố.
Panagiotis Kanavos

Câu trả lời:


1

Không có hướng dẫn nào tốt hơn những gì nhóm .NET đã làm:

  • ChannelReader.ReadAllAsync trả về mộtIAsyncEnumerable<T>
  • Trong EF Core 3, kết quả được trả về dưới dạng IAsyncEnumerablebằng cách gọi AsAsyncEnumerable ()
  • Trong System.Linq.Async, ToAsyncEnumerable () chuyển đổi IEnumerables, Nhiệm vụ và Quan sát thành IAsyncEnumerables
  • Tất cả các nhà khai thác khác System.Linq.Asyncgiữ lại tên của họ. Không có SelectAsynchoặc SelectAsAsyncEnumerable, chỉ Select.

Trong mọi trường hợp, rõ ràng kết quả của phương pháp là gì. Trong mọi trường hợp, kết quả của phương pháp cần phải được chờ đợi await foreachtrước khi chúng có thể được sử dụng.

Vì vậy, hướng dẫn thực sự vẫn giữ nguyên - đảm bảo tên làm cho hành vi rõ ràng :

  • Khi tên đã rõ ràng, ví dụ với AsAsyncEnumerable()hoặc ToAsyncEnumerable(), không cần thêm bất kỳ hậu tố nào.
  • Trong các trường hợp khác, thêm Asynchậu tố để các nhà phát triển biết họ cần await foreachkết quả.

Các máy phân tích mã và trình tạo mã không thực sự quan tâm đến tên của các phương thức, chúng phát hiện mùi bằng cách tự kiểm tra mã. Một phân tích mã sẽ cho bạn biết rằng bạn quên chờ đợi một nhiệm vụ hoặc await foreachmột IAsyncEnumerablebất kể có bao bạn gọi các phương pháp và các biến. Một trình tạo có thể chỉ cần sử dụng sự phản chiếu để kiểm tra IAsyncEnumerablevà phát raawait foreach

Đó là máy phân tích kiểu kiểm tra tên. Công việc của họ là đảm bảo mã sử dụng một kiểu nhất quán để các nhà phát triển có thể hiểu mã. Trình phân tích kiểu sẽ cho bạn biết rằng một phương thức không tuân theo kiểu bạn đã chọn. Phong cách đó có thể là đội hoặc hướng dẫn phong cách thường được chấp nhận.

Và tất nhiên, mọi người đều biết tiền tố phổ biến cho các trường riêng tư là _:)


Cảm ơn bạn đã chỉ ra ChannelReader.ReadAllAsyncví dụ. Vì điều này xuất phát trực tiếp từ nhóm .NET, thật khó để đi sai theo sau.
hwaien

Gói phân tích thường đi kèm với một túi hỗn hợp của máy phân tích kiểu và không kiểu. Ví dụ: gói Microsoft.VisualStudio.Threading.Analyzers có bộ phân tích kiểu kiểm tra quy ước đặt tên (VSTHRD200) và bộ phân tích không kiểu phát hiện các tình huống nguy hiểm có thể dẫn đến bế tắc (VSTHRD111). Để tham khảo gói để tận dụng VSTHRD111, một dự án cũng sẽ kết thúc với VSTHRD200.
hwaien

2

Đây không phải là một phương thức không đồng bộ, vì vậy tên không nên kết thúc bằng 'Async'. Hậu tố phương thức đó là một quy ước để làm rõ rằng phương thức nên được chờ đợi hoặc kết quả được xử lý như một Nhiệm vụ.

Tôi nghĩ rằng một tên trở lại bộ sưu tập bình thường là thích hợp. GetFoos () hoặc tương tự.


Tất cả đó là những đối số cho việc sử dụng Asynchậu tố. Hậu tố được sử dụng trong chính .NET Core: ChannelReader.ReadAllAsync trả về IAsyncEnumerable. An IAsyncEnumerablephải được sử dụng với await foreach, vì vậy hậu tố có ý nghĩa.
Panagiotis Kanavos

1

Hậu tố Async và thậm chí từ khóa asyncchỉ là bản tóm tắt, nó không có ý nghĩa gì ngoài một số đối số tương thích ngược. C # có tất cả thông tin để phân biệt các hàm đó giống như nó phân biệt yieldtrong các phương thức trả vềIEnumerable , như bạn biết bạn không cần thêm bất cứ thứ gì như enumerablehoặc một số từ khóa khác vào các phương thức đó.

Bạn cần thêm hậu tố Async chỉ vì C # sẽ phàn nàn về tình trạng quá tải, vì vậy về cơ bản hai cái đó giống hệt nhau và chúng làm điều tương tự bởi tất cả các quy tắc đa hình (nếu chúng ta không tính đến yếu tố hành vi làm hỏng nghiêm trọng của con người):

public interface IMyContract
{
    Task<int> Add(int a, int b);
    int Add(int a, int b);
}

Nhưng bạn không thể viết nó như thế này vì trình biên dịch sẽ phàn nàn. Nhưng, này! Nó sẽ nuốt một thứ quái dị này:

public interface IMyContract : IEnumerable<int>, IEnumerable<long>
{
}

và thậm chí này:

public interface IMyContractAsync
{
    Task<int> Add(int a, int b);
}

public interface IMyContract : IMyContractAsync
{
    int Add(int a, int b);
}

Vì vậy đây là nó. Bạn thêm Async chỉ để dừng trình biên dịch để khiếu nại. Không làm rõ mọi chuyện. Không làm cho họ tốt hơn. Nếu không có khiếu nại - không có lý do để thêm nó, vì vậy trong trường hợp của bạn, tôi sẽ tránh điều này, trừ khi nó trở thành Task<IAsyncEnumerable>.

PS: Để hiểu rõ hơn về toàn bộ hình ảnh ở đây - nếu đôi khi trong tương lai ai đó thêm vào các phần mở rộng phương thức máy tính lượng tử C # (fantazy, huh) sẽ hoạt động theo mô hình khác nhau, có lẽ chúng ta sẽ cần một hậu tố khác như Quant hoặc một cái gì đó, bởi vì C # sẽ phàn nàn khác. Nó sẽ chính xác cùng một phương pháp nhưng nó sẽ làm việc theo cách khác. Cũng giống như đa hình làm. Cũng giống như giao diện làm.


1
Xin lỗi, nhưng tôi cảm thấy tôi không đồng ý, đó không phải là cách tiếp cận thực sự khác nhau - đó là nhiều hơn - dự kiến ​​sẽ được gọi khác nhau, và kết quả sẽ được thu thập khác nhau. Đó là sự khác biệt nhất giữa async và non-async. Tôi tin tưởng mạnh mẽ việc thêm async là rõ ràng. Tôi nghĩ đây cũng là khuyến nghị của Microsoft.
madoxdev

Vâng, tôi đồng ý nhưng không đồng ý. Giống như trong Danh sách, bạn có chức năng Sắp xếp đơn giản và không "QuickSort", "RadixSort", "BubbleSort", "MixOfSorts". Chúng khác nhau, được sử dụng bởi phạm vi nhưng rõ ràng không cần làm rõ ngoài mục đích gỡ lỗi (ý tôi là bạn chỉ quan tâm đến chúng khi chúng ảnh hưởng đến hiệu suất / tài nguyên). Trong trường hợp này, DoThing và DoT BreathAsync có cùng một tình huống so với bất kỳ thuật toán tạo khuôn mẫu nào khác, họ không cần làm rõ, nó có được hỗ trợ hay không và chỉ hữu ích để gỡ lỗi.
eocron

Đó là khác nhau, có nghĩa là async - người gọi heu đảm bảo bạn xử lý việc này một cách chính xác. Thực tế của nhiệm vụ về hưu là không đủ để nói rằng phương pháp đó thực sự là Async. Người gọi phải biết để chờ đợi hoặc sử dụng GetAwaiter (). GetResilt () để có cùng số lượng. Đó là khác nhau không phải làm thế nào những điều được thực hiện bên trong.
madoxdev

Đây hoàn toàn là mục đích của máy phân tích mã và trình biên dịch. IntellySense có thể dễ dàng chỉ ra điều này cho bạn, không cần sử dụng bộ não của nhà phát triển để làm nổi bật hoặc thậm chí cấm sử dụng phương thức đồng bộ hóa qua phương thức không đồng bộ trong mã không đồng bộ. Từ kinh nghiệm của tôi khi sử dụng Async, tôi hiếm khi cần sử dụng GetResult (), nhưng tôi thường xuyên phải làm ô nhiễm toàn bộ cơ sở mã với Async.
eocron

Như ai đó đã đánh dấu điều đó - đó là ý kiến. Chúng ta có thể tiếp tục tin tưởng vào những gì chúng ta tin và hạnh phúc :)
madoxdev
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.