2012-03-08 38 views
10

Tôi có chức năng chấp nhận Số đếm. Tôi cần đảm bảo rằng điều tra viên được đánh giá, nhưng tôi không muốn tạo bản sao của nó (ví dụ: thông qua ToList() hoặc ToArray()) nếu tất cả đã sẵn sàng trong Danh sách hoặc một số bộ sưu tập "cố định" khác. By Frozen Tôi có nghĩa là các bộ sưu tập nơi tập hợp các mục đã được thiết lập, ví dụ: Danh sách, mảng, FsharpSet, Bộ sưu tập, vv, như trái ngược với các công cụ LINQ như Select() và where().Có thể xác định xem một số IE2umercó bị vô hiệu hóa không?

Có thể tạo hàm "ForceEvaluation" có thể xác định xem có thể đếm được số thực thi đang chờ xử lý và sau đó đánh giá số đếm được không?

public void Process(IEnumerable<Foo> foos) 
{ 
     IEnumerable<Foo> evalutedFoos = ForceEvaluation(foos) 
     EnterLockedMode(); // all the deferred processing needs to have been done before this line. 
     foreach (Foo foo in foos) 
     { 
      Bar(foo); 
     } 
} 

public IEnumerable ForceEvaluation(IEnumerable<Foo> foos) 
{ 
     if(??????) 
     { return foos} 
     else 
     {return foos.ToList()} 

} 

}

Sau khi một số nghiên cứu chi tiết tôi đã nhận ra rằng đây là khá nhiều không thể trong bất kỳ ý nghĩa thực tiễn, và sẽ yêu cầu kiểm tra mã phức tạp của mỗi iterator. Vì vậy, tôi sẽ đi với một biến thể của câu trả lời của Mark và tạo ra một danh sách trắng của các loại an toàn được biết đến và chỉ cần gọi ToList() bất cứ điều gì không có trên đó không có trong danh sách trắng.

Cảm ơn tất cả vì sự giúp đỡ của bạn.

Chỉnh sửa * Sau khi phản ánh nhiều hơn, tôi đã nhận ra rằng điều này tương đương với sự cố tạm dừng. Rất không thể.

+0

Bạn có thể kiểm tra 'if (foos là IList )', mặc dù đây có thể là một hack. Điều này làm việc ngay cả khi 'foos' là một mảng. – dasblinkenlight

+0

Bạn muốn điều gì xảy ra với 'while (true) {yield return 1; } '? Tôi nghi ngờ bạn có thể làm điều gì sai. Ngoài ra, những người đang đề xuất 'if (foos là' ​​IList ')' không nhận ra rằng 'IList ' * có thể * cũng được thực hiện một cách lười biếng. – jason

+0

"Trì hoãn thi hành" là một thuật ngữ rộng lớn, nó có thể có nghĩa là bất cứ thứ gì. Bạn đang cố gắng xử lý một trường hợp cụ thể (tức là 'IQueryable'). Nếu vậy, hãy kiểm tra kiểu 'IQueryable' và chạy' ToList() 'để chống lại điều đó). –

Trả lời

5

Bạn thể thử một tấm séc hy vọng chống lại IList<T> hoặc ICollection<T>, nhưng lưu ý rằng những thể vẫn được thực hiện một cách lười biếng - nhưng nó là rất hiếm, và LINQ không làm điều đó - nó chỉ sử dụng vòng lặp (không lười biếng bộ sưu tập). Vì vậy:

var list = foos as IList<Foo>; 
if(list != null) return list; // unchanged 
return foos.ToList(); 

Lưu ý rằng điều này khác với thông thường .ToList(), mang đến cho bạn trở lại một danh sách khác nhau mỗi lần, để đảm bảo không có gì bất ngờ xảy ra.

Hầu hết các loại bộ sưu tập cụ thể (bao gồm T[]List<T>) đáp ứng IList<T>. Tôi không quen thuộc với các bộ sưu tập F # - bạn cần phải kiểm tra.

+0

FSharpSet là không thay đổi. :) –

+0

@ Jason bất biến những thứ vẫn có thể thực hiện 'IList '; ví dụ: 'ReadOnlyCollection '. Câu hỏi đặt ra là: * FSharpSet có làm như vậy không? * –

+0

Ah, tôi đã nói rằng nó không thay đổi được, vì vậy không có cơ hội nào có xử lý trì hoãn. :) Ngoài ra, không có nó không thực hiện IList . –

1

Tôi sẽ tránh nó nếu bạn muốn chắc chắn rằng nó "bị đóng băng". Cả hai phần tử Mảng và Danh sách <> đều có thể được thay đổi bất kỳ lúc nào (ví dụ: bộ sưu tập nổi tiếng "thay đổi trong khi lặp lại"). Nếu bạn thực sự cần phải chắc chắn IEnumerable được đánh giá và không thay đổi bên dưới mã của bạn hơn là sao chép tất cả các mục vào Danh sách/Mảng của riêng bạn.

Có thể có các lý do khác để thử nó - tức là một số hoạt động trong thời gian chạy thực hiện kiểm tra đặc biệt để thu thập là một mảng để tối ưu hóa chúng. Hoặc có phiên bản đặc biệt cho giao diện chuyên dụng như ICollection hoặc IQueryable ngoài IEnumerable chung.

EDIT: Ví dụ về thay đổi bộ sưu tập trong suốt lặp:

IEnumerable<T> collectionAsEnumrable = collection; 
foreach(var i in collectionAsEnumrable) 
{ 
    // something like following can be indirectly called by 
    // synchronous method on the same thread 
    collection.Add(i.Clone()); 
    collection[3] = 33; 
} 
+0

Lời khuyên tốt, chính bản thân nó được phân lập thành một sợi đơn (và các mục bên trong được quản lý bằng hệ thống khóa, đó là lý do tôi cần đảm bảo toàn bộ danh sách được đánh giá trước) –

+0

Luồng không liên quan đến việc thu thập đã thay đổi. Nó có thể không áp dụng cho trường hợp cụ thể của bạn, nhưng hãy cẩn thận rằng "không thực hiện trì hoãn" không giống như "sẽ không thay đổi ngẫu nhiên trong khi lặp lại". –

0

Nếu chúng ta có thể sử dụng một wrapper trong trường hợp của bạn, bạn có thể làm một cái gì đó như thế này

public class ForceableEnumerable<T> : IEnumerable<T> 
{ 
    IEnumerable<T> _enumerable; 
    IEnumerator<T> _enumerator; 

    public ForceableEnumerable(IEnumerable<T> enumerable) 
    { 
     _enumerable = enumerable; 
    } 

    public void ForceEvaluation() 
    { 
     if (_enumerator != null) { 
      while (_enumerator.MoveNext()) { 
      } 
     } 
    } 

    #region IEnumerable<T> Members 

    public IEnumerator<T> GetEnumerator() 
    { 
     _enumerator = _enumerable.GetEnumerator(); 
     return _enumerator; 
    } 

    #endregion 

    #region IEnumerable Members 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    #endregion 
} 

Hoặc thực hiện các phương pháp có hiệu lực như thế này nếu bạn muốn đánh giá trong mọi trường hợp

public void ForceEvaluation() 
{ 
    if (_enumerator == null) { 
     _enumerator = _enumerable.GetEnumerator(); 
    } 
    while (_enumerator.MoveNext()) { 
    } 
} 

EDIT:

Nếu bạn muốn đảm bảo rằng các liệt kê được đánh giá một lần duy nhất trong mọi trường hợp, bạn có thể thay đổi GetEnumerator để

public IEnumerator<T> GetEnumerator() 
{ 
    if (_enumerator == null) } 
     _enumerator = _enumerable.GetEnumerator(); 
    } 
    return _enumerator; 
} 
+0

Hãy sửa tôi nếu tôi sai, nhưng điều này không chỉ đánh giá lại lần thứ hai khi tôi gọi foreach (t trong ForceableEnumerable )? –

+0

Có. Tôi giả định rằng bạn sẽ gọi 'ForceEvaluation()' sau khi điều tra có thể được đánh giá, không phải trước đây.Câu hỏi của bạn là về buộc đánh giá, không cản trở nó. Tuy nhiên, bạn có thể thay đổi 'GetEnumerator' nếu bạn muốn làm điều đó. Xem chỉnh sửa của tôi. –

+0

Lưu ý rằng tôi không sử dụng 'foreach' nhưng' while', không tự động tạo một điều tra viên. –

6

Cái gì mà làm việc cho tôi cách:

IEnumerable<t> deffered = someArray.Where(somecondition); 

if (deffered.GetType().UnderlyingSystemType.Namespace.Equals("System.Linq")) 
{ 
    //this is a deffered executin IEnumerable 
} 
Các vấn đề liên quan