2010-01-06 44 views
59

Một trong các phương pháp mở rộng trên IEnumerable<T>.AsEnumerable(). Phương thức này chuyển đổi đối tượng enumerable nó đã được gọi vào một thể hiện của IEnumerable<T>. Tuy nhiên, vì đối tượng phải triển khai IEnumerable<T> để áp dụng cho phương thức mở rộng này, chuyển đổi thành IEnumerable<T> là một vấn đề đơn giản khi truyền tới IEnumerable<T>. Câu hỏi của tôi là tại sao phương pháp này tồn tại?Tại sao sử dụng .AsEnumerable() thay vì truyền tới IEnumerable <T>?

Ví dụ:

List<string> strings = new List<string>() { "test", "test2", "test3" }; 
IEnumerable<string> stringsEnum1 = strings.AsEnumerable(); 
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings; 

Trong ví dụ trên, stringsEnum1stringsEnum2 là tương đương. Điểm của phương pháp mở rộng là gì?

Chỉnh sửa: Theo hệ quả, tại sao lại có phương pháp .AsQueryable() khi truyền tới IQueryable<T> tương đương?

Trả lời

74

Khả năng đọc là vấn đề chính ở đây. Hãy xem xét rằng

Table.AsEnumerable().Where(somePredicate) 

là xa dễ đọc hơn so

((IEnumerable<TableObject>)Table).Where(somePredicate). 

Hoặc tưởng tượng muốn thực hiện một phần của các truy vấn trên SQL Server và phần còn lại trong ký ức:

Table.Where(somePredicate) 
    .Select(someProjection) 
    .AsEnumerable() 
    .SomethingElse() 

so

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate) 
             .Select(someProjection)) 
             .SomethingElse() 

Bây giờ, về lý do tại sao một phương pháp như vậy hữu ích ở tất cả mọi người nghĩ về ví dụ về một Table trong một LINQ to SQL DataContext. Dưới dạng TableIQueryable, nó thực hiện IEnumerable. Khi bạn gọi phương thức Where trên một số Table và liệt kê thông qua các kết quả, mã được thực hiện mà cuối cùng làm cho một câu lệnh SQL được thực hiện trên một máy chủ SQL. Điều gì AsEnumerable không có nghĩa là, không, tôi không muốn sử dụng nhà cung cấp LINQ to SQL để thực thi Where, tôi muốn sử dụng triển khai LINQ to Objects của Where.

Do đó liệt kê trên

Table.Where(somePredicate) 

gây ra một truy vấn được thực hiện trên một SQL Server trong khi liệt kê trên

Table.AsEnumerable().Where(somePredicate) 

mang bảng đại diện bởi Table vào bộ nhớ và thực thi các chức năng Where trong bộ nhớ (và không phải trên Máy chủ SQL!)

Đây là điểm của AsEnumerable: cho tất cả bạn có thể ẩn một triển khai cụ thể của các phương thức IEnumerable và thay vào đó sử dụng triển khai chuẩn.

+1

Điều này đúng - nhưng sẽ không chuyển sang 'IEnumerable ' cung cấp cùng một kết quả? –

+6

Tôi tìm thấy 'Table.AsEnumerable(). Where (somePredicate)' để dễ đọc hơn nhiều so với '((IEnumerable ) Table) .Where (somePredicate)'. – jason

+0

Chúng tôi biết nó dễ đọc hơn, nhưng chúng có tương đương không? –

3

Đó chỉ là cách đẹp nhất và ngắn nhất để truyền tới IEnumerable. Nếu bạn nhìn vào nó trong Reflector, bạn có thể thấy nó không làm gì ngoại trừ trả về đối tượng như một IEnumerable.

Từ MSDN:

Các AsEnumerable (Tất TSource) (IEnumerable (Tất TSource)) phương pháp không có tác dụng nào khác ngoài việc thay đổi kiểu thời gian biên dịch mã nguồn từ một loại mà cụ IEnumerable (Of T) tới IEnumerable (Of T) .

+0

Tôi không chắc điều này là đúng. Tôi tin rằng nó là để làm với việc thực hiện truy vấn. – gingerbreadboy

+0

@runrunraygun: Nó có bởi vì nó làm cho nó dễ dàng hơn để chuyển đổi từ một IQueryable thành IEnumerable, ví dụ, nhưng nó thường không làm bất cứ điều gì đặc biệt ngoại trừ hơn cast nó để IEnumerable.Nếu bạn không tin tôi, hãy kiểm tra trong Reflector ;-) –

+0

@runrunraygun: Tôi đã cung cấp báo giá từ liên kết mà bạn đã cung cấp để chứng minh điều này ;-) –

2

Như bạn nói, nếu một kiểu đã thực hiện IEnumerable<T> thì không có sự khác biệt chức năng nào giữa truyền tới giao diện hoặc gọi phương thức AsEnumerable.

đoán của tôi, và nó chỉ là một phỏng đoán, đó là kêu gọi AsEnumerable cải thiện khả năng đọc và giữ lại chữ ký thông thạo các phương pháp khuyến nông LINQ khác:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty); 

// vs 

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty); 

Nó cũng cho phép loại mà không thực hiện IEnumerable<T>-for example, DataTable - để có phiên bản riêng của tiện ích mở rộng AsEnumerable. Điều này cho phép bạn tiếp tục sử dụng cùng một mẫu trong truy vấn đối với các loại đó - mặc dù đó là phương thức AsEnumerable khác mà bạn đang gọi - mà không cần phải lo lắng về việc loại thực sự có thực hiện IEnumerable<T> hay không.

+0

Nhưng những loại không thực hiện IEnumerable không có quyền truy cập vào phương pháp mở rộng AsEnumerable - vì vậy các loại đó không được bao gồm trong tập hợp các loại mà câu hỏi này có liên quan. Tuy nhiên, điểm tốt. –

+0

@Erik: Quan điểm của tôi là bạn có thể sử dụng * cùng một mẫu * khi truy vấn 'DataTable', ví dụ, mặc dù' DataTable' không thực hiện 'IEnumerable ': 'var query = yourDataTable.AsEnumerable(). Chọn (x => x ["YourColumn"]) 'vv http://msdn.microsoft.com/en-us/library/system.data.datatableextensions.asenumerable.aspx – LukeH

+0

Có - đó là lý do tại sao tôi đã bình chọn cho bạn. =) –

13

Tôi đã nghĩ ra một lý do ngoài khả năng đọc, mặc dù liên quan đến việc triển khai truy vấn: sử dụng LINQ to Objects trên các kiểu ẩn danh được trả về thông qua nhà cung cấp LINQ khác. Bạn không thể truyền sang một loại ẩn danh (hoặc một tập hợp các loại ẩn danh), nhưng bạn có thể sử dụng .AsEnumerable() để thực hiện dàn diễn viên cho bạn.

Ví dụ:

// Get an IQueryable of anonymous types. 
var query = from p in db.PeopleTable /* Assume Linq to SQL */ 
      select new { Name = p.Name, Age = p.Age }; 

// Execute the query and pull the results into an IEnumerable of anonymous types 
var enum = query.AsEnumerable(); 

// Use Linq to Objects methods to further refine. 
var refined = from p in enum 
       select new 
       { 
        Name = GetPrettyName(p.Name), 
        DOB = CalculateDOB(p.Age, DateTime.Now) 
       }; 

Rõ ràng lý do ở đây là chúng ta muốn sử dụng cái gì đó như LINQ to SQL để kéo xuống một số hồ sơ vào một loại vô danh, sau đó thực hiện một số logic tùy chỉnh (mà có thể không thực hiện được thông qua LINQ to SQL) bằng cách sử dụng LINQ to Objects ở phía máy khách.

Truyền tới IEnumerable<_anon> là không thể, vì vậy .AsEnumerable() là cách duy nhất để thực hiện.

Cảm ơn tất cả mọi người đã trả lời giúp tôi chia sẻ nội dung này với nhau. =)

2

Loại ẩn danh là lý do chính để cung cấp các loại phương pháp mở rộng này. (bạn không thể sử dụng các loại vô danh trong các tham số generics) Nhưng một cuộc gọi phương thức có thể sử dụng suy luận kiểu cho phép bạn bỏ qua chỉ định loại trong các tham số chung.

2

Nếu có một phương thức trên đối tượng có cùng tên như một phương pháp mở rộng LINQ, nó ẩn phương thức mở rộng. Sử dụng AsEnumerable cho phép bạn nhận được phần mở rộng.

Điều này có vẻ mới trong SP1.

Hôm qua tôi đã có một dòng mã chiết xuất định danh thành viên từ một bảng dữ liệu: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId) 
); 

mà chỉ làm việc tốt cho đến khi tôi cài đặt SP1. Bây giờ nó sẽ không làm việc trừ khi nó đọc

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId) 
); 

Edit: Tôi tìm thấy lý do thực sự

Đó là để bạn có thể sử dụng cả hai phương thức từ xa (ví dụ WHERE trong một câu lệnh SQL) và các phương pháp địa phương cùng một câu lệnh linq. Nếu không sử dụng AsEnumerable (tức là chỉ cần đúc), nó sẽ làm cho trình tạo truy vấn cố gắng tạo một cây biểu thức cho thực thi từ xa có chứa phương thức cục bộ. Đặt AsEnumerable vào truy vấn sẽ khiến phần còn lại của truy vấn đó được thực thi cục bộ trên các kết quả của truy vấn từ xa.

Từ https://msdn.microsoft.com/en-us/library/bb335435(v=vs.110).aspx

Một loại Bảng đại diện cho một bảng cơ sở dữ liệu có thể có một phương pháp ở đâu mà có đối số vị như một cây biểu thức và chuyển đổi cây to SQL để thực hiện từ xa. Nếu thực thi từ xa không được mong muốn, ví dụ vì biến vị ngữ gọi một phương thức cục bộ, phương thức AsEnumerable có thể được sử dụng để ẩn các phương thức tùy chỉnh và thay vào đó làm cho các toán tử truy vấn chuẩn có sẵn.

3

Khi tôi đang đọc sách C# 6.0 in a Nutshell. Dưới đây là ví dụ về AsEnumerable trong sách.


Mục đích là để đúc một chuỗi IQueryable<T>-IEnumerable<T>, buộc các nhà khai thác truy vấn tiếp theo liên kết với các nhà khai thác Enumerable thay vì khai thác queryable. Điều này làm cho phần còn lại của truy vấn thực thi tại địa phương.

Để minh họa, giả sử chúng tôi có một bảng MedicalArticles trong SQL Server và muốn sử dụng LINQ to SQL hoặc EF để truy xuất tất cả các bài viết về cúm có tóm tắt chứa ít hơn 100 từ. Đối với vị thứ hai, chúng ta cần một biểu thức chính quy:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b"); 

var query = dataContext.MedicalArticles 
      .Where (article => article.Topic == "influenza" && 
      wordCounter.Matches (article.Abstract).Count < 100); 

Vấn đề là SQL Server không hỗ trợ biểu thức thông thường, vì vậy các nhà cung cấp LINQ-to-db sẽ ném một ngoại lệ, phàn nàn rằng các truy vấn không thể được dịch sang SQL. Chúng ta có thể giải quyết việc này bằng cách truy vấn theo hai bước: đầu tiên lấy tất cả các bài viết về cúm thông qua một LINQ truy vấn SQL, và sau đó lọc nước cho tóm tắt dưới 100 chữ:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b"); 

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles 
    .Where (article => article.Topic == "influenza"); 

IEnumerable<MedicalArticle> localQuery = sqlQuery 
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100); 

Với AsEnumerable, chúng ta có thể làm tương tự trong một truy vấn:

var query = dataContext.MedicalArticles 
     .Where (article => article.Topic == "influenza") 
     .AsEnumerable() 
     .Where (article => wordCounter.Matches (article.Abstract).Count < 100); 

Cách thay thế để gọi AsEnumerable là gọi ToArray hoặc ToList. Ưu điểm của AsEnumerable là nó không ép buộc thực thi truy vấn ngay lập tức, cũng như không tạo ra bất kỳ cấu trúc lưu trữ nào.

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