2009-05-23 25 views
22

Đây là chức năng của tôi:LINQ: Cách khai báo IEnumerable [AnonymousType]?

private IEnumerable<string> SeachItem(int[] ItemIds) 
    { 
     using (var reader = File.OpenText(Application.StartupPath + @"\temp\A_A.tmp")) 
     { 
      var myLine = from line in ReadLines(reader) 
         where line.Length > 1 
         let id = int.Parse(line.Split('\t')[1]) 
         where ItemIds.Contains(id) 
         let m = Regex.Match(line, @"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)") 
         where m.Success == true 
         select new { Text = line, ItemId = id, Path = m.Groups[2].Value }; 
      return myLine; 
     } 
    } 

tôi nhận được một lỗi biên dịch, bởi vì "myLine" không phải là một IEnumerable [chuỗi] và tôi không biết làm thế nào để viết IEnumerable [Anonymous]

"Can not chuyển đổi hoàn toàn loại 'System.Collections.Generic.IEnumerable [AnonymousType # 1]' thành 'System.Collections.Generic.IEnumerable [string]' "

Trả lời

15

Bạn không thể tuyên bố IEnumerable<AnonymousType> vì loại không có tên (biết) ở thời gian xây dựng. Vì vậy, nếu bạn muốn sử dụng kiểu này trong một khai báo hàm, hãy làm cho nó trở thành một kiểu bình thường. Hoặc chỉ cần sửa đổi truy vấn của bạn để trả lại số IENumerable<String> và kết hợp với loại đó.

Hoặc trả lại IEnumerable<KeyValuePair<Int32, String>> bằng cách sử dụng câu lệnh chọn sau.

select new KeyValuePair<Int32, String>(id, m.Groups[2].Value) 
+2

Khi bạn nói "select new {...}", bạn đã tạo một AnonymousType. –

+0

Không, nhưng bạn có thể trả về IEnumerable và sử dụng thủ thuật CastByExample.Không được đề nghị mặc dù –

0

Một điều cần nhớ là LINQ câu lệnh sử dụng deferred execution. Điều đó có nghĩa là câu lệnh LINQ của bạn không thực sự thực thi cho đến khi bạn lặp lại nó trong câu lệnh foreach hoặc bạn gọi phương thức .ToList() trên myLine.
Ví dụ bạn hãy thử thay đổi:

return myLine; 

Để:

return myLine.ToList(); 
+0

Vấn đề là trong định nghĩa thủ tục, vì vậy nó cần được "Danh sách tin SeachItem (int [] ItemIds)", nhưng điều đó không làm việc, hoặc. "Không thể ngầm chuyển đổi loại Liệt kê [AnoynymousType # 1] thành List [string] .Xin vui lòng cho tôi biết nếu nó không thể thực hiện được. :) –

8

Phương pháp chữ ký trên SearchItem chỉ ra rằng phương pháp này trả về một IEnumerable<string> nhưng kiểu nặc danh tuyên bố trong truy vấn LINQ của bạn không phải là loại string. Nếu bạn muốn giữ cùng chữ ký phương thức, bạn phải thay đổi truy vấn của mình để chỉ chọn string s. ví dụ.

return myLine.Select(a => a.Text); 

Nếu bạn khăng khăng trả lại dữ liệu bằng cách truy vấn của bạn lựa chọn, bạn có thể trả về một IEnumerable<object> nếu bạn thay thế tuyên bố return của bạn với

return myLine.Cast<object>(); 

Sau đó, bạn có thể tiêu thụ các đối tượng sử dụng phản ánh. Nhưng thực sự, nếu bạn sắp tiêu thụ một loại ẩn danh bên ngoài phương thức được khai báo, bạn nên định nghĩa một lớp có phương thức trả về một lớp IEnumerable của lớp đó. Các loại ẩn danh tiện lợi nhưng chúng có thể bị lạm dụng.

5

Chức năng của bạn đang cố gắng để trở lại IEnumerable < chuỗi >, khi tuyên bố LINQ bạn đang thực hiện là thực sự trả lại một IEnumerable <T> trong đó T là một thời gian biên dịch tạo kiểu. Các kiểu ẩn danh không phải lúc nào cũng ẩn danh, khi chúng lấy một loại cụ thể, cụ thể sau khi mã được biên dịch. Tuy nhiên,

Các loại ẩn danh, vì chúng không có thời gian cho đến khi được biên dịch, chỉ có thể được sử dụng trong phạm vi chúng được tạo.Để hỗ trợ cho nhu cầu của bạn trong ví dụ mà bạn cung cấp, tôi sẽ nói là giải pháp đơn giản nhất là để tạo ra một thực thể đơn giản mà các cửa hàng kết quả của truy vấn của bạn:

public class SearchItemResult 
{ 
    public string Text { get; set; } 
    public int ItemId { get; set; } 
    public string Path { get; set; } 
} 

public IEnumerable<SearchItemResult> SearchItem(int[] itemIds) 
{ 
    // ... 
    IEnumerable<SearchItemResult> results = from ... select new SearchItemResult { ... } 
} 

Tuy nhiên, nếu mục tiêu cuối cùng của bạn không phải là để lấy một số loại đối tượng, và bạn chỉ quan tâm đến, nói rằng, con đường ... sau đó bạn vẫn có thể tạo ra một IEnumerable < chuỗi >:

IEnumerable<string> lines = from ... select m.Groups[2].Value; 

tôi hy vọng rằng sẽ giúp làm sáng tỏ sự hiểu biết của bạn về LINQ, enumerables, và các loại vô danh. :)

+1

FYI: Không đúng khi nói rằng các loại vô danh là duy nhất cho phạm vi mà chúng được tạo ra. tương đương trong cùng một ssembly nhưng được sử dụng trong hai phương pháp khác nhau thực sự được chia sẻ trên các phương thức đó. Có những cách thông minh để tận dụng lợi thế của thực tế này, nhưng tôi sẽ không khuyên họ; nếu bạn cần chia sẻ một loại trên hai phương pháp, hãy làm cho nó trở nên danh nghĩa. –

+0

Đó có phải là Eric Lippert của Microsoft nổi tiếng không? Bây giờ bạn đã mở túi, bạn phải để con mèo ra ngoài. Làm thế nào để bạn chia sẻ một loại ẩn danh trên phạm vi? ;) – jrista

+1

Rất cẩn thận. Bạn phải sử dụng các thủ thuật suy luận kiểu gõ goofy và các biến bên ngoài bị bắt. Có lẽ tôi sẽ làm một blog trên đó một thời gian. –

6

Tôi không nhất thiết phải giới thiệu này ... Nó là một loại lật đổ của hệ thống kiểu nhưng bạn có thể làm điều này:

1) thay đổi phương pháp chữ ký của bạn để trở IEnumerable (một trong những phi generic)

2) thêm một helper cast by example:

public static class Extensions{ 
    public static IEnumerable<T> CastByExample<T>(
      this IEnumerable sequence, 
      T example) where T: class 
    { 
     foreach (Object o in sequence) 
      yield return o as T; 
    } 
} 

3) sau đó gọi phương thức một cái gì đó như thế này:

var example = new { Text = "", ItemId = 0, Path = "" }; 
foreach (var x in SeachItem(ids).CastByExample(example)) 
{ 
    // now you can access the properties of x 
    Console.WriteLine("{0},{1},{2}", x.Text, x.ItemId, x.Path); 
} 

Và bạn đã hoàn tất.

Điều quan trọng đối với điều này là nếu bạn tạo một loại ẩn danh có cùng thứ tự, loại và tên thuộc tính ở hai nơi thì các loại sẽ được sử dụng lại. Biết điều này bạn có thể sử dụng Generics để tránh sự phản chiếu.

Hope this helps Alex

+0

Thông minh. Nhưng thực sự, nếu ai đó sẽ đi đến nhiều rắc rối, họ chỉ nên đóng gói dữ liệu trong một lớp được khai báo. – jason

+2

Như tôi đã nói tôi không đề xuất điều này;) –

+0

... thật thú vị khi bạn chỉ gặp rắc rối này một lần, tức là khi bạn có phương pháp mở rộng trợ giúp, điều này rất dễ sử dụng lặp đi lặp lại. Không giống như các loại cán mới ở khắp mọi nơi. Dù sao đây là công việc cho Tuple trong .NET 4.0 –

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