2010-05-20 35 views
9

Tôi không hiểu làm thế nào hiện tại có thể là null và cuối cùng có thể là một đối tượng trong khi cuối cùng là một chức năng LINQ. Tôi nghĩ rằng cuối cùng sử dụng GetEnumerator và tiếp tục đi cho đến khi hiện tại == null và trả về đối tượng. Tuy nhiên, bạn có thể thấy hàm GetEnumerator đầu tiên(), hiện tại là null và cuối cùng bằng cách nào đó trả về một đối tượng.LINQ Last() hoạt động như thế nào?

Phương thức linq Last() hoạt động như thế nào?

var.GetEnumerator().Current 
var.Last() 
+5

Bạn đang bối rối với các điều tra viên của họ. Hãy tưởng tượng một cuốn sách có các trang được đánh số. Đó là một chuỗi. Hãy tưởng tượng một dấu trang, đánh dấu một trang cụ thể. Đó là một điều tra viên. Bạn có thể có một trăm dấu trang trong một cuốn sách nếu muốn, tất cả đều đánh dấu các địa điểm khác nhau. Gọi GetEnumerator.Hiện tại đang yêu cầu trang đánh dấu khi bạn chưa đặt nó vào cuốn sách; đừng làm thế. –

Trả lời

16

Từ sử dụng Reflector trên System.Core.dll:

public static TSource Last<TSource>(this IEnumerable<TSource> source) 
{ 
    if (source == null) 
    { 
     throw Error.ArgumentNull("source"); 
    } 
    IList<TSource> list = source as IList<TSource>; 
    if (list != null) 
    { 
     int count = list.Count; 
     if (count > 0) 
     { 
      return list[count - 1]; 
     } 
    } 
    else 
    { 
     using (IEnumerator<TSource> enumerator = source.GetEnumerator()) 
     { 
      if (enumerator.MoveNext()) 
      { 
       TSource current; 
       do 
       { 
        current = enumerator.Current; 
       } 
       while (enumerator.MoveNext()); 
       return current; 
      } 
     } 
    } 
    throw Error.NoElements(); 
} 
7

Last() sẽ gọi GetEnumerator(), sau đó tiếp tục gọi MoveNext()/Current cho đến khi MoveNext() lợi nhuận sai, lúc này nó sẽ trả về giá trị cuối cùng của Current lấy ra. Nullity không được sử dụng như một terminator trong chuỗi, nói chung.

Vì vậy, việc thực hiện có thể là một cái gì đó như thế này:

public static T Last<T>(this IEnumerable<T> source) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext()) 
     { 
      throw new InvalidOperationException("Empty sequence"); 
     } 
     T value = iterator.Current; 
     while (iterator.MoveNext()) 
     { 
      value = iterator.Current; 
     } 
     return value; 
    } 
} 

(Điều này có thể được thực hiện với một vòng lặp foreach, nhưng trên cho thấy sự tương tác một cách rõ ràng hơn này cũng bỏ qua khả năng truy cập vào các yếu tố cuối cùng trực tiếp. .)

+1

Ngoài ra, không 'collection.GetEnumerator(). Current' trả về" đối tượng trước phần tử đầu tiên của bộ sưu tập "? Không phải là một phần của hợp đồng mà bạn cần phải gọi MoveNext ít nhất một lần trước khi hiện tại có giá trị xác định? –

+1

@Lasse: Có, mặc dù IIRC khối lặp được tạo bởi C# bỏ qua điều này: ( –

+1

'MoveNext()' * được * gọi trước 'Current', trong khối' if'. – Jason

2

Các đã ry giá trị đầu tiên của một điều tra viên, trước bất kỳ MoveNext(), là, trong trường hợp của một mảng, mục tại chỉ mục -1.
Bạn phải thực hiện MoveNext một lần để nhập bộ sưu tập thực tế.
này được thực hiện để các nhà xây dựng của các điều tra viên không làm nhiều công việc, và do đó các cấu trúc có giá trị:

while (enumerator.MoveNext()) { 
     // Do Stuff 
} 

if(enumerator.MoveNext()) { 
     // Do Stuff 
} // This is identical to Linq's .Any(), essentially. 
1

Hãy nhớ rằng gọi GetEnumerator không thường/thiết trả lại tương tự Enumerator mỗi lần. Ngoài ra, kể từ khi thing.GetEnumerator() trả về một Enumerator mới sẽ bắt đầu chưa được khởi tạo (bạn chưa gọi MoveNext()), thing.GetEnumerator().Currentsẽ luôn không có định nghĩa.

(Tôi nghĩ ...)

+1

Kết quả là không xác định, thường là null, –

1

Nếu bạn có một cái nhìn tại IEnumerator Interface vào phần chú thích, họ sẽ nêu sau đây:

Ban đầu, các điều tra viên là vị trí trước phần tử đầu tiên trong bộ sưu tập. Tại vị trí này, Hiện tại không xác định. Vì vậy, bạn phải gọi MoveNext để chuyển điều tra viên đến phần tử đầu tiên của bộ sưu tập trước khi đọc giá trị của Hiện tại.

Vì vậy, bạn phải gọi MoveNext() một lần để nhận mục đầu tiên. Nếu không, bạn sẽ nhận được chỉ là không có gì.

+0

Điều này giải thích những gì đã xảy ra với quá trình suy nghĩ của tôi. –

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