2008-11-08 24 views
54

Tôi vừa nhận thấy rằng một mảng đa chiều trong C#, không thực hiện IEnumerable<T>, trong khi nó thực hiện IEnumerable. Đối với mảng một chiều, cả hai IEnumerable<T>IEnumerable đều được triển khai.Tại sao C# mảng đa chiều không triển khai IEnumerable <T>?

Tại sao sự khác biệt này? Nếu mảng đa chiều là IEnumerable, chắc chắn nó cũng nên triển khai phiên bản chung? Tôi nhận thấy điều này bởi vì tôi đã cố gắng sử dụng một phương pháp mở rộng trên một mảng đa chiều, mà không trừ khi bạn sử dụng Cast<T> hoặc tương tự; vì vậy tôi chắc chắn có thể thấy một đối số để tạo mảng đa chiều thực hiện IEnumerable<T>.

Để làm rõ câu hỏi của tôi trong mã, tôi mong chờ đoạn mã sau để in true gấp bốn lần, trong khi nó thực sự in true, false, true, true:

int[] singleDimensionArray = new int[10]; 
int[,] multiDimensional = new int[10, 10]; 

Debug.WriteLine(singleDimensionArray is IEnumerable<int>); 
Debug.WriteLine(multiDimensional is IEnumerable<int>); 
Debug.WriteLine(singleDimensionArray is IEnumerable); 
Debug.WriteLine(multiDimensional is IEnumerable); 
+3

Cũng xấu xí: 'multiDimensional' là ngầm mui trần đến ** phi generic ** loại 'System.Collections.IList' (đơn giản vì 'System.Array' thực hiện giao diện đó). Vì vậy, bạn có thể nói 'System.Collections.IList mdCast = multiDimensional;'. Sau đó, sử dụng trình chỉ mục một tham số trên 'mdCast' sẽ không thành công khi chạy. Xem [doc on MSDN] (http://msdn.microsoft.com/en-us/library/bb494734.aspx). Lưu ý kiểu ngoại lệ, 'ArgumentException'. Rất xấu. –

Trả lời

39

Các CLR có hai loại khác nhau của các mảng: vectơ mà được đảm bảo để có một chiều với một thấp hơn giới hạn từ 0, và mảng tổng quát hơn có thể có khác không giới hạn và một cấp bậc khác hơn 0.

Từ phần 8.9.1 của spec CLI:

Bên cạnh đó, một vector tạo ra với yếu tố loại T, thực hiện các giao diện System.Collections.Generic.IList<U> (§8.7), nơi U: = T.

Tôi phải nói rằng có vẻ khá lạ đối với tôi.Vì nó đã thực hiện IEnumerable Tôi không thấy lý do tại sao nó không nên thực hiện IEnumerable<T>. Nó sẽ không có ý nghĩa nhiều để thực hiện IList<T>, nhưng giao diện chung đơn giản sẽ là tốt.

Nếu bạn muốn điều này, bạn có thể gọi Cast<T> (nếu bạn đang sử dụng .NET 3.5) hoặc viết phương thức của riêng bạn để lặp qua mảng. Để tránh việc đúc, bạn phải viết phương pháp của riêng mình, tìm thấy giới hạn dưới/trên của mỗi thứ nguyên và tìm nạp những thứ theo cách đó. Không dễ chịu lắm.

+0

Ngoài ra, Đặc tả Ngôn ngữ C# (phiên bản 4.0) đề cập trong đoạn 6.1.6 ** chỉ ** chuyển đổi mảng đơn chiều thành 'IList <>' và các giao diện cơ bản của nó. Nhưng đó là một sự xấu hổ rằng một 'T [,]' không phải là một 'ICollection '. –

5

Mảng nhiều chiều thực không mảng cho mục đích của hệ thống phân cấp thừa kế. Họ là một loại hoàn toàn riêng biệt. Ngoài ra, loại này không có hỗ trợ tốt từ khung vì hai lý do có thể:

  • Nó không thực sự hữu ích. Việc sử dụng thực sự duy nhất cho mảng đa chiều là ma trận. Đối với hầu hết mọi thứ, các cấu trúc dữ liệu khác (ví dụ: các mảng có răng cưa) phù hợp hơn.
  • Nó không tầm thường để đưa ra các phương pháp chung, hữu ích cho các cấu trúc này.

Trong trường hợp IEnumerable, điều này nên được thực hiện như thế nào, tức là các yếu tố nào cần được liệt kê? Không có thứ tự vốn có trong mảng đa chiều.

+1

Nhưng mảng đa chiều thừa hưởng lớp Array –

+12

Nhưng IEnumerable * được * triển khai, do đó, hoặc IEnumerable sẽ có thể sử dụng cùng một logic hoặc IEnumerable không nên được triển khai. – recursive

+9

Điều này được xác định rõ ràng trong Đặc tả ngôn ngữ C#. Trong phiên bản 4.0, nó là đoạn 8.8.4, nó nói: ** Thứ tự mà foreach truyền qua các phần tử của một mảng, như sau: Đối với các phần tử mảng một chiều được di chuyển theo thứ tự tăng dần, bắt đầu với chỉ mục 0 và kết thúc với chỉ số Độ dài - 1. Đối với mảng đa chiều, các phần tử được truyền qua sao cho các chỉ số của thứ nguyên ngoài cùng bên phải được tăng lên trước, sau đó là thứ nguyên bên trái tiếp theo, và bên trái. ** Tiếp theo là một ví dụ nó thậm chí còn rõ ràng hơn cho mảng đa chiều. –

0

mảng Jagged không hỗ trợ IEnumerable<int> một trong hai, bởi vì cấu trúc đa chiều là không thực sự là một mảng của một loại, họ là một mảng của một mảng của một loại:

int[] singleDimensionArray = new int[10]; 
int[][] multiJagged = new int[10][]; 

Debug.WriteLine(singleDimensionArray is IEnumerable<int>); 
Debug.WriteLine(multiJagged is IEnumerable<int[]>); 
Debug.WriteLine(singleDimensionArray is IEnumerable); 
Debug.WriteLine(multiJagged is IEnumerable); 

Prints sự thật, sự thật, sự thật , thật.

Note: int[,] không phải là một IEnumerable<int[]>, đó là vì những lý do nêu trong câu trả lời khác, cụ thể là không có cách nào tổng quát để biết được chiều hướng để lặp qua. Với các mảng răng cưa, không có nhiều chỗ để giải thích vì cú pháp khá rõ ràng về nó là một mảng các mảng.

+0

Đệ quy không được thảo luận. – recursive

+0

@recursive: cổ vũ, cố định. Chỉ là một trường hợp trộn lẫn thuật ngữ của tôi. –

+3

Anh ta không nói về mảng bị lởm chởm, anh ta nói về một mảng đa chiều. –

10

Có một workaround: bạn có thể chuyển đổi bất kỳ mảng đa chiều đối với một IEnumerable

public static class ArrayExtensions 
{ 
    public static IEnumerable<T> ToEnumerable<T>(this Array target) 
    { 
     foreach (var item in target) 
      yield return (T)item; 
    } 
} 
+0

Công cụ tuyệt vời! Tôi đã phải xác định rõ ràng loại chung mặc dù, do đó, sử dụng trở thành: myArray.ToEnumerable () – angularsen

+23

Điều này giống như 'IEnumerable.Cast '. –

+0

@Markus Brilliant, đây phải là câu trả lời được chấp nhận. Là một sang một bên, nếu mảng có thể chứa giá trị rỗng nó sẽ trở thành Matrix.Cast (Of T) .Where (Chức năng (x) x IsNot Không có gì) – smirkingman

0

suy nghĩ ngược lại. Mảng 2d đã tồn tại. Chỉ cần liệt kê nó. Tạo một mảng 2d với điểm số và vị trí của một mảng hoặc nhãn hiệu ban đầu, bao gồm các giá trị trùng lặp.

int[] secondmarks = {20, 15, 31, 34, 35, 50, 40, 90, 99, 100, 20}; 

IEnumerable<int> finallist = secondmarks.OrderByDescending(c => c); 

int[,] orderedMarks = new int[2, finallist.Count()]; 

Enumerable.Range(0, finallist.Count()).ToList().ForEach(k => {orderedMarks[0, k] = (int) finallist.Skip(k).Take(1).Average(); 
orderedMarks[1, k] = k + 1;}); 

Enumerable.Range(0, finallist.Count()).Select(m => new {Score = orderedMarks[0, m], Place = orderedMarks[1, m]}).Dump(); 

Kết quả:

Score Place 

100  1 
99  2 
90  3 
50  4 
40  5 
35  6  
34  7  
31  8  
20  9  
20  10 
15  11 
+0

Vui lòng thêm giải thích mã của bạn. –

1

Zero, ràng buộc mảng chiều đơn thực hiện cả hai IEnumerableIEnumerable<T>, nhưng mảng đa chiều, không may, chỉ IEnumerable thực hiện. Các "workaround" bởi @ Jader Dias thực sự chuyển đổi một mảng đa chiều để IEnumerable<T> nhưng với một chi phí rất lớn: mọi yếu tố của một mảng sẽ được đóng hộp.

Đây là một phiên bản đó sẽ không gây ra đấm bốc cho mỗi phần tử:

public static class ArrayExtensions 
{ 
    public static IEnumerable<T> ToEnumerable<T>(this T[,] target) 
    { 
     foreach (var item in target) 
      yield return item; 
    } 
} 
Các vấn đề liên quan