Không thể sử dụng mảng "nội tuyến" trong C #?


89

Hãy tưởng tượng bạn có cái này ở đâu đó

public static T AnyOne<T>(this T[] ra) where T:class
    {
    int k = ra.Length;
    int r = Random.Range(0,k);
    return ra[r];
    }

hoặc thậm chí chỉ cái này

public static string OneOf(this string[] strings)
    {
    return "a";
    }

Sau đó, tất nhiên bạn có thể làm điều này ...

string[] st = {"a","b","c"};
string letter = st.AnyOne();

... thật tuyệt. NHƯNG. Có vẻ như bạn KHÔNG thể làm điều này:

string letter = {"a","b","c"}.AnyOne();

hoặc thực sự có lẽ điều này

string letter = ( {"a","b","c"} ).AnyOne();

hoặc bất cứ điều gì khác mà tôi đã thử.

Trong thực tế (1) tại sao người ta không thể làm điều đó? và (2) tôi có thiếu thứ gì không, bạn sẽ làm thế nào nếu có cách?


5
Tôi không chắc câu hỏi trùng lặp là phù hợp, OP không hỏi về trình khởi tạo mảng, nhưng tại sao trình biên dịch sẽ không nhận ra đối tượng là một mảng cho đến khi nó được gán.
— Ron Beyer

4
Tôi không quen thuộc với thuật ngữ C #, nhưng tôi tin rằng điều này thường được gọi là một nghĩa đen hoặc một mảng hơn là một nội dòng .
— chi

3
Phần tử cú pháp đó là một bộ khởi tạo mảng hoặc bộ khởi tạo tập hợp , tùy thuộc vào ngữ cảnh mà nó được sử dụng. Trong cả hai trường hợp, nó đều không được phân loại là một biểu thức .
— Eric Lippert

Câu trả lời:


129

Bạn phải tạo mảng trước bằng cách sử dụng new[].

string letter = (new[] {"a","b","c"}).AnyOne();

Như @hvd đã đề cập, bạn có thể làm điều này mà không cần parantheses (..), tôi đã thêm paranthes vì ​​tôi nghĩ nó dễ đọc hơn.

string letter = new[] {"a","b","c"}.AnyOne();

Và bạn có thể chỉ định kiểu dữ liệu new string[]như trên các câu trả lời khác đã được đề cập.


Bạn không thể chỉ làm {"a","b","c"}, bởi vì bạn có thể coi nó như một cách để điền mảng chứ không phải để tạo nó.

Một lý do khác là trình biên dịch sẽ bị nhầm lẫn, không biết phải tạo cái gì, ví dụ: a string[]{ .. }hoặc a List<string>{ .. }.

Chỉ cần sử dụng new[]trình biên dịch là có thể biết theo kiểu dữ liệu ( ".."), giữa {..}, những gì bạn muốn ( string). Điều quan trọng là []bạn muốn có một mảng.

Bạn thậm chí không thể tạo một mảng trống với new[].

string[] array = new []{ }; // Error: No best type found for implicity-typed array

13
Bạn không cần những dấu ngoặc đơn đó. string letter = new[] {"a","b","c"}.AnyOne();là tốt. Nếu bạn muốn chúng, nếu bạn nghĩ rằng nó dễ đọc hơn với dấu ngoặc đơn, chúng có giá trị, nhưng trong trường hợp đó, tôi nghĩ ít nhất cần nhắc lại rằng đó là một lựa chọn có ý thức từ phía bạn, nó không bị ép buộc bởi ngôn ngữ.

Tôi biết về cú pháp new [] {1,2}, nhưng có cú pháp nào đơn giản hơn không? Một cái gì đó giống như [1, 2]?
— seguso

51

(1) tại sao người ta không thể làm điều đó? {"a","b","c"}.AnyOne();

Đường thẳng này:

string[] st = {"a","b","c"};

là viết tắt của một biểu thức tạo mảng tương đương (theo ILSpy )

string[] st = new string[]  {"a","b","c"};

Điều này string[] st = {"a","b","c"} chỉ có thể được sử dụng tại thời điểm khai báo , bạn không thể không sử dụng nó ở nơi khác, thậm chí bạn không thể làm:

string[] st;
st = {"a", "b", "c"}; //Error

Nó được giải thích trong Phần 7.6.10.4 cho biểu thức Tạo Mảng trong đặc tả ngôn ngữ C #.

Vì vậy, điều này "{"a", "b", "c"}"một mình mà không sử dụng trong khai báo có nghĩa là không có gì. Do đó, bạn không thể sử dụng nó với phương thức mở rộng của mình, vì phương thức mở rộng của bạn hoạt động trên một mảng.

(2) Tôi đang thiếu một cái gì đó, làm thế nào bạn sẽ làm điều đó nếu có một cách?

Đã được đề cập trong câu trả lời của @ adricadar , bạn có thể làm:

(new[] {"a","b","c"}).AnyOne();

hoặc là

(new string[] {"a","b","c"}).AnyOne();

48

Tôi đẩy lùi các câu hỏi "tại sao không" bởi vì đầu tiên, các câu trả lời hầu như không bao giờ thỏa mãn - bạn đã có câu trả lời "tính năng không theo cách bạn muốn vì đặc điểm kỹ thuật không nói những gì bạn muốn nó nói" , mà tôi tưởng tượng không phải là một câu trả lời đặc biệt thỏa mãn. Thứ hai, nhóm thiết kế không cần phải giải thích lý do tại sao thế giới không theo cách bạn muốn; các tính năng không tồn tại miễn phí và sau đó được thiết kế bằng ngôn ngữ; thay vào đó, các tính năng phải được xác minh trước, sau đó được thiết kế trong.

Vì vậy, hãy cố gắng làm cho câu hỏi "tại sao không" của bạn trở nên rõ ràng hơn một chút. Tính năng hiện có là "một bộ khởi tạo mảng có thể được sử dụng (a) ở phía bên phải của dấu bằng trong một lần khởi tạo hoặc (b) ở bên phải của một cấu trúc đối tượng kiểu mảng." Tính năng được đề xuất là: "một trình khởi tạo mảng cũng có thể được sử dụng như một biểu thức". Câu hỏi đặt ra là "Eric sẽ đưa ra những lời chỉ trích nào về tính năng được đề xuất?"

Lời chỉ trích đầu tiên mà tôi đưa ra là không rõ loại biểu thức là gì. Trong bộ khởi tạo biến, bạn có kiểu của biến và trong biểu thức tạo đối tượng, bạn có kiểu của đối tượng; từ cả hai điều này, chúng ta có thể suy ra kiểu của mảng đã xây dựng. Nếu không có một trong hai gợi ý, chúng ta nên suy ra loại nào?

Trong C # 1.0, khi tính năng này được thêm vào, đã có tổng số không các suy luận kiểu 0 được thực hiện bằng ngôn ngữ. Một nguyên tắc thiết kế trong những ngày đầu của C # là "không có gì ngạc nhiên", và trình biên dịch không "quá thông minh". Nếu nhà phát triển dự định một biểu thức thuộc một kiểu cụ thể thì kiểu đó phải rõ ràng theo một cách nào đó trong biểu thức. Khi bạn nói

new double[] { 1, 2, 3.4 }

nó là khá rõ ràng loại nào được dự định. Tương tự

new Animal[] { cat, dog, null }

Tính năng được đề xuất vi phạm nguyên tắc này. Biểu thức phải có một kiểu, nhưng nó không có nghĩa là rõ ràng kiểu của đối số là gì

M({cat, dog, null})

Hơn nữa: Giả sử chúng ta có hai quá tải M, một trong số đó chiếm một mảng Animalvà một nạp một mảng IPet. Quá tải nào củaM được áp dụng? Một trong những chuyển đổi có tốt hơn chuyển đổi khác không? Các loại của các phần tử là Catvà Dog; nó có hợp lý để suy ra một loại thậm chí không xuất hiện ở đó? Đây đều là những câu hỏi mà nhóm thiết kế phải cân nhắc, và đây là những câu hỏi không có câu trả lời rõ ràng. Tính năng được đề xuất dẫn chúng ta vào vùng nước sâu theo thứ tự khá ngắn.

Bây giờ, C # 3.0 giải quyết vấn đề này vì C # 3.0 đã thêm nhiều tính năng mà trình biên dịch thay mặt cho nhà phát triển nhập các loại. Các nguyên tắc trước đây về "không gây ngạc nhiên" và "quy tắc đơn giản" mâu thuẫn với các nguyên tắc thiết kế khác cần thiết để làm cho LINQ hoạt động. Tính năng bạn đề xuất có được thêm vào C # 3.0 không?

Nó có thể đã được. Tính năng thực sự được thêm vào C # 3.0 là:

new[] { x, y, z }

suy ra kiểu của mảng bằng cách sử dụng thuật toán: lấy biểu thức cho các phần tử có kiểu, xác định kiểu nào trong số đó là kiểu tổng quát nhất duy nhất mà tất cả các biểu thức khác có thể chuyển đổi được và nếu kiểu như vậy tồn tại, hãy chọn kiểu đó. Nếu không sẽ tạo ra lỗi,

Tính năng đó có thể đã được nới lỏng hơn nữa để trở thành new[]tùy chọn. Điều này đã không được thực hiện.

Bây giờ, nếu bạn yêu cầu tôi trong khung thời gian C # 3.0 phê bình tính năng được đề xuất, tôi sẽ chỉ ra rằng (1) trình biên dịch C # 3.0 đã có nguy cơ trượt lịch trình cho toàn bộ bản phát hành, vì vậy chúng ta đừng thêm bất kỳ điều gì nữa gánh nặng thiết kế, triển khai và thử nghiệm đối với một tính năng hoàn toàn không cần thiết giúp tiết kiệm cho người dùng sáu lần nhấn phím và (2) C # 3.0 cũng đã thêm các bộ khởi tạo bộ sưu tập:

new List<int>() { 10, 20, 30 }

tại sao {10, 20, 30}tự động phải là một mảng ? Tại sao nó không phải là một List<int>? Hoặc bất kỳ một trong số các loại khác? Tại sao thiên vị đối với mảng? Hãy nhớ rằng, một khi chúng ta chọn lưu trữ cú pháp cho các mảng, chúng ta sẽ bị mắc kẹt với nó mãi mãi . Nó có thể không bao giờ là bất cứ thứ gì khác, vì vậy tính năng được đề xuất không chỉ không cần thiết mà còn ngăn chặn các tính năng có thể có trong tương lai có vẻ hợp lý.

Tóm lại: tính năng được đề xuất đã trực tiếp vi phạm một số nguyên tắc thiết kế của C # 1.0. Nó không thêm gì ngoài gánh nặng không cần thiết cho C # 3.0. Trong tất cả các phiên bản của ngôn ngữ kể từ C # 3.0, tính năng được đề xuất không có lý do chính đáng để khuyên bạn nên dành thời gian, công sức và tiền bạc cho nó thay vì nhiều tính năng khác xứng đáng hơn.

Do đó, không có tính năng như vậy.


Heh, bạn cần phải nhận được một t-shirt in với "thế giới không phải là cách bạn muốn nó được" in trên nó :)
— slugster

6
@JoeBlow: Trước hết, bạn rất được hoan nghênh. Về "tại sao không" - bình luận của bạn minh họa vấn đề một cách độc đáo. Khi một số người đặt câu hỏi "tại sao" họ đang tìm kiếm một lời biện minh hợp lý . Một số người đang tìm kiếm một lời biện minh thực dụng . Và bạn dường như đang tìm kiếm dòng đặc tả mô tả quy tắc . Nó mơ hồ đến mức rất khó để tạo ra một câu trả lời tốt nhắm vào câu hỏi thực sự trong tâm trí người hỏi. Những câu hỏi "tại sao không" thậm chí còn tệ hơn vì chúng là những câu hỏi mơ hồ về những thứ thậm chí không tồn tại .
— Eric Lippert

3
@EricLippert Nó thậm chí còn tệ hơn câu hỏi về những thứ _might_ tồn tại : Nếu bạn đang làm việc trên phần mềm với một nhóm, toàn bộ nhóm dành mỗi ngày trong năm để suy nghĩ về các tính năng và cân bằng hậu quả. Các quyết định được thực hiện. Yêu cầu tính năng và 'lý do' yêu cầu giải thích. Tuy nhiên, tại sao không ngụ ý rằng ai đó chỉ muốn một điều gì đó được thực hiện, điều này về cơ bản đặt câu hỏi về sự đánh giá của chính đội ngũ. Tệ hơn nữa, người đặt câu hỏi tại sao không thường biết rất ít về chủ đề này. Như vậy, tôi nghĩ rằng việc đẩy lùi câu hỏi tại sao không là hoàn toàn công bằng. Làm tốt lắm IMO.
— atlaste
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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.