Là C # trở nên khó đọc hơn?


15

Khi C # đã phát triển, nhiều tính năng ngôn ngữ đã được thêm vào. Nó đã đến mức không thể đọc được đối với tôi.

Ví dụ, xem xét đoạn mã sau đây từ mã Caliburn.Micro tại đây :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Bây giờ, đây chỉ là một ví dụ nhỏ.

Tôi có một số câu hỏi:

  • Đây có phải là một vấn đề phổ biến hoặc được biết đến?
  • Là cộng đồng C # tìm thấy giống nhau?
  • Đây có phải là một vấn đề với ngôn ngữ, hay nó là phong cách được sử dụng bởi nhà phát triển?

Có giải pháp đơn giản nào để hiểu rõ hơn về mã người khác và tránh viết mã theo cách này không?


6
Đó là chức năng lambda, chúng rất khó đọc cho đến khi bạn thực sự có được chúng.
Ryathal

9
Tôi nghĩ rằng bạn thực sự không biết cú pháp như bạn nghĩ. Với thực hành đọc ví dụ của bạn là đơn giản.
ChaosPandion

6
@Thomas Owens: Holy shit man, ít nhất hãy cho chúng tôi một chút thời gian để chỉnh sửa các câu hỏi để giữ cho chúng mở. 15 phút? Hãy đến ngay bây giờ.
Steven Evers

4
@DannyVarod: 1. "Không" không phải là câu trả lời chấp nhận được 2. Câu hỏi đã bị đóng 3. Nếu bạn đồng ý rằng câu hỏi không nên ở đây thì hãy gắn cờ / bỏ phiếu để đóng hoặc chỉnh sửa nó để tốt hơn .
Steven Evers

2
@ Thorbjørn Ravn Andersen - Người ta có thể học được gì từ một cái tát? Điều này không chứa thông tin!

Câu trả lời:


12

Ghi chú nhanh về nơi ngôn ngữ sẽ xóa nó: C # là ngôn ngữ lập trình có mục đích chung ; Không giống như C (như C ++), nó cố gắng cho sự trừu tượng hóa cao; không giống như phương ngữ Lisp, nó nhắm đến tính biểu cảm thực tế và, không giống như Java, nó được thúc đẩy mạnh mẽ hơn - Microsoft nhanh chóng đáp ứng nhu cầu.

Đó là lý do tại sao nó biến thành hỗn hợp của LINQ, lambdas và các từ khóa mới lạ - nó nhanh chóng thích nghi với các miền vấn đề mới, và đây là thực sự là một con dốc trơn tru đối với một ngôn ngữ phức tạp đến mức rất ít người có thể sử dụng nó một cách chính xác (như C ++). Đó không phải là vấn đề với chính C #, đó là vấn đề với bất kỳ ngôn ngữ nào có các mục tiêu đầy tham vọng này.

Cộng đồng nhận thức được điều này và quan trọng hơn, những người đứng sau C # nhận thức sâu sắc về điều này (một vài mục blog và podcast về những gì đằng sau những bổ sung mới trong C # 5.0 cho thấy những kẻ này muốn mọi thứ đơn giản đến mức nào). Microsoft đang cố gắng giảm tải cho chiếc flagship của mình để nó không trở thành một vấn đề: giới thiệu DLR, một điểm sáng trên các ngôn ngữ mới (F #).

Hơn nữa, tài liệu khóa học về C # (bao gồm chứng chỉ MS) đề xuất các cách sử dụng khác nhau (nhưng nhất quán) cho C # tùy thuộc vào vấn đề - chọn vũ khí của bạn và tuân theo: LINQ kiểu thông thạo về một số vấn đề, lambdas với TPL trên các vấn đề khác, rõ ràng -old cho hầu hết.


2
Bạn có thể giải thích ý của bạn với "không giống như phương ngữ Lisp, nó nhắm đến tính biểu cảm thực tế" không?
Giorgio

27

Số C # đang cho chúng ta nhiều tùy chọn để viết mã ngắn gọn hơn. Điều này có thể được sử dụng hoặc lạm dụng. Nếu được sử dụng đúng cách, nó làm cho mã dễ đọc hơn. Nếu sử dụng không đúng cách nó có thể làm cho mã khó đọc hơn. Nhưng các lập trình viên tồi luôn có kỹ năng viết mã khó đọc.

Hãy lấy hai ví dụ, cả hai đều dựa trên các ví dụ được tìm thấy trên StackOverflow liên quan đến cùng một vấn đề, tìm các loại có thuộc tính cụ thể:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

IMHO, cú pháp mới làm cho nó dễ đọc hơn rất nhiều.


cú pháp mới thực sự dễ đọc hơn, nhưng dễ hiểu hơn, như trong những gì đang diễn ra dưới mui xe? Ví dụ đầu tiên là dễ hiểu, thứ hai là dễ đọc hơn.
nawfal

4
@nawfal Theo kinh nghiệm của tôi, mọi người gặp khó khăn hơn nhiều khi hiểu các cấu trúc "lợi nhuận" so với các câu lệnh linq; Vì vậy, tôi sẽ tranh luận thứ hai là dễ đọc và dễ hiểu hơn. Không thực sự cho bạn biết những gì đang diễn ra dưới mui xe, vì cả hai ánh xạ đến trừu tượng khác xa với cách bộ xử lý thực sự hoạt động.
Chuu

Thành thật mà nói, tôi không thích LINQ vì nó trừu tượng hóa các vòng lặp và khuyến khích các lập trình viên lấy dữ liệu trong các truy vấn LINQ riêng biệt (với các vòng lặp riêng biệt trong nền) thay vì một vòng lặp với lõi phức tạp hơn.
mg30rg

8

Khi LINQ được thêm vào, nó đã phổ biến một kiểu mã hóa liên quan đến rất nhiều chuỗi phương thức kiểu Fluent và truyền lambdas làm tham số.

Phong cách này rất mạnh mẽ một khi bạn cảm thấy thoải mái với nó. Tuy nhiên nó có thể bị lạm dụng để tạo mã khá khó đọc. Tôi không nghĩ rằng ví dụ của bạn quá tệ, mặc dù việc thụt lề là ngẫu nhiên (và đó là một đặc điểm chung của kiểu mã này, Visual Studio không tự động thụt lề nó một cách nhất quán).

Tại nơi làm việc của tôi, chúng tôi đã thảo luận về phong cách mã hóa này khi xem xét các tiêu chuẩn mã hóa của chúng tôi vào đầu năm nay và chúng tôi đã quyết định khuyến khích các nhà phát triển phá vỡ mã như thế: cụ thể, không tạo và khởi tạo các đối tượng ẩn danh trong lệnh gọi hàm. Vì vậy, mã của bạn sẽ trở thành:

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);

1
Không có gì lạ khi tạo ra một loại mới, và sau đó tự động chuyển sang loại đó ngay sau đó?
DeadMG

Có lẽ, tôi chỉ cắt đoạn mã mà không có bất kỳ cân nhắc thực sự nào về ý định của nó. Chỉ muốn tách tất cả các phần khởi tạo thành các dòng mã riêng của chúng để hiển thị ảnh hưởng gì đến giao diện của mã.
Carson63000

Tôi đổ lỗi cho sự thụt lề :(). Dưới đây là bản gốc: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/...

1
Tôi hoàn toàn đồng ý với câu trả lời đó. Vấn đề trong đoạn mã mẫu không phải là cú pháp C # mới, mà người viết đã cố gắng khéo léo với mã "một lớp". Có một quy tắc cũ có từ UNIX: mọi thứ chỉ nên làm một việc và làm tốt. Nó áp dụng cho lớp, nó áp dụng cho phương thức và tất nhiên, nó chắc chắn áp dụng cho các dòng mã.
Laurent Bourgault-Roy

4

Mã trong ví dụ của bạn không dễ đọc vì

  • Nó trộn nhiều khái niệm (Thành phần, Danh mục, Tập hợp, Tập hợp, ComposableParts ...) với một số mức lồng nhau trong khi mã gọi thường chỉ phải xử lý một cấp. Trông hơi giống như một sự vi phạm Luật Demeter chỉ với các cuộc gọi phương thức lồng nhau thay vì một chuỗi các thuộc tính phụ. Điều này làm vẩn đục ý định đằng sau dòng mã phần nào.

  • Có một cách sử dụng đơn lẻ kỳ lạ - AssemblySource.Instance.Select()ngụ ý rằng Instance là một IEnumerable, điều này hơi khó xử.

  • Biến x trong biểu thức lambda là xấu. Nói chung, bạn cố gắng cung cấp cho các biến lambda của bạn ý định tiết lộ tên - giúp người đọc xác định loại dữ liệu đó là về chức năng gì, ngay cả khi anh ta không quen thuộc với lambdas.

  • Không rõ lý do tại sao bạn nên lọc với OfType<T>()một bộ sưu tập các đối tượng bạn vừa mới ...

Tuy nhiên, tất cả các lỗ hổng này liên quan đến cách nhà phát triển ban đầu viết mã và cách anh ta thiết kế nó một cách biểu cảm. Đây không phải là một cái gì đó cụ thể cho các phiên bản mới nhất của C # và sự xuất hiện của biểu thức lambda.

Tổng quát hơn, sẽ hữu ích nếu người đọc mã của bạn biết cách lambdas hoạt động, nhưng với cách đặt tên rõ ràng, bạn hầu như luôn quản lý để làm cho mã của mình có thể đọc được ngay cả với người có nền tảng 3.5.


2

Bất cứ khi nào một phiên bản mới của ngôn ngữ phổ biến đạt được các cấu trúc mới, các cuộc tranh luận tương tự sẽ nảy sinh.

Tôi cũng đã có những nghi ngờ này từ nhiều năm trước (http://gsscoder.blogspot.it/2009/08/use-csharps-features-w chính.html).

Theo tôi C # đang phát triển một cách thanh lịch và nhất quán.

Mã trong mẫu của bạn không phức tạp, có thể bạn không được sử dụng với các biểu thức lamda.

Vấn đề là C # không chỉ là ngôn ngữ hướng đối tượng, giờ đây nó còn hỗ trợ cả các cấu trúc chức năng (http://archive.msdn.microsoft.com/FeftalCSharp/).


1

Phải mất một chút thời gian để hiểu cú pháp Lambda, và tôi đến từ nền tảng toán học và vật lý. Nó hơi giống với các biểu thức toán học, mặc dù họ sử dụng -> thay vì =>:

Ví dụ toán học f (x): = x-> x + 2 cái này đọc là "Hàm f của x được định nghĩa là x ánh xạ trên x + 2

c # ví dụ del myDelegate = x => x +2; cái này đọc là "myDelegate là một hàm sao cho myDelegate (x) ánh xạ x lên x + 2

Sự không nhất quán giữa toán học và lập trình không thực sự có ích, mặc dù tôi cho rằng -> đã được đưa vào C ++ như là "tài sản của" (urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathologists_symbols


"mặc dù tôi cho rằng -> đã được đưa vào C ++ như là" tài sản của "(urrgghh!)" Như thể! K ++ kém có nghĩa là "tài sản của phân bổ động ...". Nếu một cái gì đó không năng động, bạn sử dụng khoảng thời gian như trong mọi ngôn ngữ khác.
mg30rg
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.