14

Tôi luôn nghĩ rằng nó hoạt động tốt theo cả hai cách. Sau đó, đã làm xét nghiệm này và nhận ra nó không được phép trên tái tập:Tại sao bộ khởi tạo bộ sưu tập về chuyển nhượng lại không được phép?

int[] a = {0, 2, 4, 6, 8}; 

hoạt động tốt nhưng không:

int [ ] a; 
a = { 0, 2, 4, 6, 8 }; 

Bất kỳ lý do kỹ thuật cho việc này? Tôi nghĩ tôi sẽ hỏi về nó ở đây, bởi vì hành vi này là những gì tôi mong đợi một cách trực quan.

+4

Bạn có thể thấy điều này hữu ích: http://stackoverflow.com/questions/7351453/why-cant-i-use-the-array-initializer-with-an-implicitly-typed-variable (không hoàn toàn trùng lặp) –

Trả lời

20

Trước hết, chúng ta hãy điều khoản chính xác. Đó không phải là bộ sưu tập . Đó là bộ khởi tạo mảng . Bộ khởi tạo bộ sưu tập luôn theo sau một hàm tạo cho một loại bộ sưu tập. Trình khởi tạo mảng chỉ hợp pháp trong bộ khởi tạo khai báo cục bộ hoặc trường hoặc trong biểu thức tạo mảng.

Bạn hoàn toàn chính xác cần lưu ý rằng đây là quy tắc lẻ. Hãy để tôi mô tả tính kỳ lạ của nó chính xác:

Giả sử bạn có phương thức M lấy một mảng int. Tất cả đây là những quy phạm pháp luật:

int[] x = new[] { 10, 20, 30 }; 
int[] y = new int[] { 10, 20, 30 }; 
int[] z = new int[3] { 10, 20, 30 }; 
M(new[] { 10, 20, 30 }); 
M(new int[] { 10, 20, 30 }); 
M(new int[3] { 10, 20, 30 }); 

Nhưng

int[] q = {10, 20, 30}; // legal! 
M({ 10, 20, 30 }); // illegal! 

Nó có vẻ như một trong hai "đơn độc" mảng initializer nên được quy phạm pháp luật ở khắp mọi nơi rằng "trang trí" một là, hoặc hư không. Thật kỳ lạ khi có biểu thức giả này chỉ hợp lệ trong trình khởi tạo, không phải ở bất kỳ nơi nào khác mà biểu thức là hợp pháp.

Trước khi cả hai chỉ trích và bảo vệ lựa chọn này, tôi muốn nói điều đó trước hết, sự khác biệt này là một tai nạn lịch sử. Không có lý do thuyết phục nào cho nó. Nếu chúng ta có thể loại bỏ nó mà không phá vỡ mã, chúng tôi sẽ làm như vậy. Nhưng chúng ta không thể. Chúng tôi đã thiết kế C# từ đầu một lần nữa hôm nay tôi nghĩ rằng tỷ lệ cược là tốt mà các "đơn độc" khởi tạo mảng mà không "mới" sẽ không phải là một cú pháp hợp lệ. Vì vậy, trước tiên, hãy để tôi đưa ra một số lý do tại sao các bộ khởi tạo mảng KHÔNG được phép như là các biểu thức và nên được cho phép trong các bộ khởi tạo biến cục bộ. Sau đó, tôi sẽ đưa ra một số lý do cho điều ngược lại.

Lý do tại sao initializers mảng không được phép như biểu thức:

Mảng initializers vi phạm sở hữu tốt đẹp mà { luôn có nghĩa là giới thiệu của một khối mới của mã. Trình phân tích cú pháp phục hồi lỗi trong IDE phân tích cú pháp khi bạn đang gõ thích sử dụng dấu ngoặc nhọn như một cách thuận tiện để báo khi nào câu lệnh chưa hoàn thành; nếu bạn thấy:

if (x == M(
{ 
    Q(

Sau đó, nó là khá dễ dàng cho các biên tập mã để đoán rằng bạn đang thiếu )) trước {. biên tập viên sẽ giả định rằng Q( là sự khởi đầu của một tuyên bố và nó bị thiếu kết thúc.

Nhưng nếu trình khởi tạo mảng là biểu thức pháp lý thì có thể là số bị thiếu là )})){}sau số Q.

Thứ hai, bộ khởi tạo mảng làm biểu thức vi phạm nguyên tắc tốt đẹp là tất cả phân bổ đống có "mới" trong chúng ở đâu đó.

Lý do tại sao initializers mảng nên được cho phép trong lĩnh vực và địa phương initializers:

Hãy nhớ rằng initializers mảng đã được thêm vào ngôn ngữ trong v1.0, người dân địa phương trước khi ngầm đánh máy, các loại vô danh, hoặc suy luận kiểu trên mảng. Quay trở lại những ngày chúng tôi không có thú vị "mới [] {10, 20, 30}" cú pháp, như vậy mà không initializers mảng bạn phải nói:

int[] x = new int[] { 10, 20, 30 }; 

mà dường như rất dư thừa! Tôi có thể thấy lý do tại sao họ muốn có được "int mới []" ra khỏi đó.

Khi bạn nói

int[] x = { 10, 20, 30 }; 

nó không phải là cú pháp mơ hồ; trình phân tích cú pháp biết rằng đây là một bộ khởi tạo mảng và không phải là sự khởi đầu của một khối mã (không giống như trường hợp tôi đã đề cập ở trên.) Cũng không phải là loại không rõ ràng; rõ ràng rằng initializer là một mảng ints từ ngữ cảnh.

Vì vậy, đối số đó biện minh cho lý do tại sao bộ khởi tạo mảng C# 1.0 được phép trong trình khởi tạo cục bộ và trường nhưng không được phép trong ngữ cảnh biểu thức.

Nhưng đó không phải là thế giới chúng ta đang có trong ngày hôm nay. Chúng ta đã thiết kế điều này từ đầu ngày hôm nay có lẽ chúng ta sẽ không có các bộ khởi tạo mảng không có "mới". Ngày nay, tất nhiên chúng tôi nhận ra rằng giải pháp tốt hơn là:

var x = new[] { 10, 20, 30 }; 

và biểu thức đó hợp lệ trong mọi ngữ cảnh. Bạn có thể rõ ràng gõ nó vào bên "tuyên bố" hoặc bên "khởi tạo" của = nếu bạn thấy phù hợp, hoặc bạn có thể để trình biên dịch suy ra các loại của một trong hai bên hoặc cả hai.

Vì vậy, tổng hợp, có, bạn đúng là không nhất quán rằng các trình khởi tạo mảng chỉ có thể trong khai báo cục bộ và trường chứ không phải trong ngữ cảnh biểu thức. Có một lý do tốt cho điều đó mười năm trước, nhưng trong thế giới hiện đại với suy luận kiểu, không còn lý do chính đáng nào cho nó nữa. Nó chỉ là một tai nạn lịch sử vào thời điểm này.

+4

Chà. StackOverflow sẽ tuyệt vời hơn 1000 lần nếu bạn đăng câu trả lời cho mọi câu hỏi C#. –

+0

@JimSchubert: Tôi đánh giá cao tình cảm rất nhiều nhưng tôi đảm bảo với bạn rằng điều đó sẽ tồi tệ hơn. Tôi không biết câu trả lời cho 99% câu hỏi C# trên trang web! Tôi là một chuyên gia về lịch sử, thiết kế và thực hiện ngôn ngữ; hẹp tập trung vào đó trong sáu năm qua có nghĩa là có rất nhiều lĩnh vực lập trình C# thực tế mà tôi biết bên cạnh không có gì về. –

+0

Dù sao, tình yêu đọc câu trả lời của bạn! – Matthias

1

nó nhất là:

int [] a;// ={1,2,3,4}; 
    a = new [] {1, 2, 3, 4}; 

Trong VB hoạt động theo cách tương tự như khai, dễ dàng hơn xD

Dim a() As Integer '={1,2,3,4} 
a = {1, 2, 3, 4} 
+1

Anh ấy hỏi tại sao hạn chế này tồn tại. –

+0

Cảm ơn, thêm 'new []' trước đây, làm cho nó hoạt động. Nhưng tại sao hạn chế này tồn tại khi nó có thể hoạt động mà không có nó (giống như khi bạn gán nó lần đầu) –

+0

Vâng, tôi nghĩ nó chỉ dành cho ngôn ngữ, vì trong VB nó có thể được thực hiện, chúng ta nên xem mã MSIL tạo ra sự khác biệt là gì . – Piyey

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