2010-09-23 29 views

Trả lời

12
finiteList.Reverse().Take(count).Reverse(); 

hoặc

finiteList.Skip(finiteList.Count() - count) 

Có một số chi phí trong việc này do đó, một phương pháp tùy chỉnh sẽ tốt hơn.

Cập nhật: Một phương pháp tùy chỉnh

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (count < 0) throw new ArgumentOutOfRangeException("count"); 

     if (count == 0) yield break; 

     var queue = new Queue<T>(count); 

     foreach (var t in source) 
     { 
      if (queue.Count == count) queue.Dequeue(); 

      queue.Enqueue(t); 
     } 

     foreach (var t in queue) 
      yield return t; 
    } 
} 

Cập nhật: Thay đổi mã một littlebit với ý tưởng từ dtb's trả lời :-)

Comment Bear: Nhìn này ví dụ:

var lastFive = Enumerable.Range(1, 10).TakeLast(5); 
var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way 

Queue<int> q = (Queue<int>)lastFive2; 
q.Dequeue(); 

//Is lastFive2 still last five? no... 

Bạn có thể pote ntially thay đổi các giá trị của lastFive2 và do đó phương pháp tiếp cận đó có thể không an toàn hoặc ít nhất nó không phải là cách chức năng.

Để Bear:

Những gì tôi có nghĩa là về an toàn là thế này:

var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way 

//some = Some method which you don't control - it could be from another assembly which represents a crazy plugin etc. 
some(lastFive2); 
//Now what? 

Trong những trường hợp này bạn sẽ phải tạo một bản sao để đảm bảo. Nhưng trong hầu hết trường hợp theo cách của bạn sẽ là tốt - và hiệu quả hơn một chút so với này để 1 :)

Một ý tưởng là sử dụng một hàng đợi mà chỉ có nội Enqueue, vv

+0

Yikes, mà sẽ công việc, nhưng nó sẽ không hiệu quả, cũng không phải thanh lịch. – Mark

+0

@Mark Khi tôi nói, tôi đồng ý với bạn :) Tôi sẽ đưa ra một phương pháp hiệu quả hơn ... –

+0

Ngoài ra, tùy thuộc vào 'finiteList' là gì, có thể hoặc thậm chí không thể liệt kê nó hai lần (đề nghị thứ hai của bạn sẽ yêu cầu) – Mark

3

MoreLINQ cung cấp một TakeLast extension method:

var last10 = finiteList.TakeLast(10); 

Chịu các yếu tố từ một bù đắp đến cùng, Enumerable.Skip nên làm các trick:

var allFromOffsetToEnd = finiteList.Skip(offset); 
+0

+1 ALMOST chính xác những gì tôi đã đưa ra - một chút muộn hơn bài viết của bạn;) –

1

Lưu ý về pe rformance. Rất nhiều câu trả lời ở đây hoạt động trên IEnumerable<> và đó có lẽ là những gì bạn cần và nên sử dụng.

Nhưng nếu tập hợp dữ liệu lớn và loại List<> hoặc tương tự, bạn có thể ngăn chặn rất nhiều iterating không cần thiết với một cái gì đó như:

// demo, no errorhandling 
public static IEnumerable<T> TakeFrom<T>(this IList<T> list, int offset) 
{ 
    for (int i = offset; i < list.Count; i += 1) 
    { 
     yield return list[i]; 
    } 
} 
+0

+1 Và như một bình luận, hầu hết các mã .Net framework (bao gồm cả phương pháp LINQ) kiểm tra xem các đối số là danh sách, mảng hoặc chỉ IEnumerables và sau đó xuất ra câu trả lời theo cách hiệu quả nhất :) Một phương pháp kết hợp sẽ tốt đẹp ... –

+0

@lassee: Tôi đã tự hỏi liệu điều này có thể hoạt động tự động bằng độ phân giải quá tải hay bạn phải viết trình bao sử dụng là/as. –

+0

Quá tải sẽ làm điều đó, nhưng một lợi thế với/là cách tiếp cận là nếu bạn đang đưa ra một nguồn bạn không phải là một danh sách, sau đó bạn sẽ phải làm cho là/như chính mình. –

2

@lasseespeholt:

public static class EnumerableExtensions 
{ 
    public static ReadOnlyEnumerable<T> AsReadOnly<T>(
     this IEnumerable<T> source) 
    { 
     return new ReadOnlyEnumerable<T>(source); 
    } 
} 

public sealed class ReadOnlyEnumerable<T> : IEnumerable<T> 
{ 
    private readonly IEnumerable<T> _source; 

    public ReadOnlyEnumerable(IEnumerable<T> source) 
    { 
     if (_source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 

     _source = source; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _source.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return _source.GetEnumerator(); 
    } 
} 

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (count < 0) throw new ArgumentOutOfRangeException("count"); 

    if (count == 0) 
     return Enumerable.Empty<T>(); 

    var queue = new Queue<T>(count); 

    foreach (var t in source) 
    { 
     if (queue.Count == count) queue.Dequeue(); 

     queue.Enqueue(t); 
    } 

    return queue.AsReadOnly(); 
} 
+0

+1 Đối với hiệu quả ... nhưng nhìn vào câu trả lời cập nhật của tôi –

+0

Ta, cảm ơn bạn rất nhiều :) –

+0

Chính xác là gì 'ReadOnlyEnumerable ' ở đó? Các giao diện 'IEnumerable' /' IEnumerable 'đã được đọc. – LukeH

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