2011-02-25 46 views
6

Hãy nói rằng, tôi có một phương pháp in như thế này:Có phương pháp chung để lặp lại và in một giá trị trong một bộ sưu tập không xác định?

private static void Print(IEnumerable items) 
{ 
    // Print logic here 
} 

Tôi muốn vượt qua một lớp bộ sưu tập phương pháp In này, mà nên in tất cả các lĩnh vực như một bảng. Ví dụ: bộ sưu tập đầu vào của tôi có thể là "Người" hoặc "Đơn hàng" hoặc "Ô tô", v.v.

Nếu tôi chuyển bộ sưu tập "Ô tô" vào phương thức In, nó sẽ in danh sách các chi tiết "Xe" như : Thực hiện, Màu sắc, Giá, Lớp, v.v.

Tôi sẽ không biết loại bộ sưu tập cho đến khi chạy. Tôi đã thử và đạt được giải pháp sử dụng TypeDescriptorsPropertyDescriptorCollection. Nhưng, tôi không cảm thấy đó là một giải pháp tốt. Có cách nào khác để đạt được điều này bằng cách sử dụng các biểu thức hoặc generics?

+1

Về là tốt nhất bạn có thể làm với Generics là có tất cả mọi thứ thực hiện một số giao diện 'IMyToString' (giả sử ToString không ghi đè trực tiếp) và sau đó lấy trong 'IEnumerable '. Có những phương pháp khác, chắc chắn, nhưng nói chung dựa vào một ánh xạ (TypeConverters là một hình thức lập bản đồ) hoặc sử dụng RTTI theo những cách khác. –

Trả lời

6

Bạn có thể thực hiện In như thế này:

static void Print<T>(IEnumerable<T> items) 
{ 
    var props = typeof(T).GetProperties(); 

    foreach (var prop in props) 
    { 
     Console.Write("{0}\t", prop.Name); 
    } 
    Console.WriteLine(); 

    foreach (var item in items) 
    { 
     foreach (var prop in props) 
     { 
      Console.Write("{0}\t", prop.GetValue(item, null)); 
     } 
     Console.WriteLine(); 
    } 
} 

Nó chỉ đơn giản lặp trên mỗi tài sản của lớp để in tên của tài sản, sau đó in trên từng hạng mục và cho mỗi mục nó in các giá trị của tính chất.

Tôi cho rằng bạn nên sử dụng Generics ở đây (trái với các đề xuất trong các câu trả lời khác); bạn muốn các mục trong bộ sưu tập là một loại duy nhất để bạn có thể in các tiêu đề bảng.

Để định dạng bảng, bạn có thể kiểm tra câu trả lời cho this question.

+0

Chính xác những gì tôi đang tìm kiếm.Cảm ơn nhiều :) –

+0

Điều này làm việc cho những gì tôi đang tìm kiếm, đó là để in 'Tên thuộc tính' của đối tượng (s) một lần, và sau đó in giá trị cho mỗi mục trong Danh sách. Cảm ơn! – Shiva

7

Ghi đè phương thức ToString() của đối tượng để bao gồm tất cả thông tin bạn muốn hiển thị và sau đó chỉ cần gọi nó bất cứ khi nào bạn muốn. Không cần cho các biểu thức hoặc generics.

+1

+1 để đề xuất mẫu đa hình tiêu chuẩn. Nếu các lớp được niêm phong, thì "Mô hình khách truy cập" (có thể sử dụng đối sánh mẫu ngược;); –

+0

Và trong mô hình khách truy cập .net là gì, nhưng phương pháp mở rộng. – Pradeep

+0

Giải pháp trên thực sự tốt. Nhưng, tôi có một vấn đề khác. Ví dụ, nếu tôi chuyển một bộ sưu tập unknow như 'var result = select p từ Persons join o trong Orders trên p.ID = o.PersonID chọn new {p.Name, o.Name};' 'Print (kết quả); 'Trong trường hợp này, làm sao tôi có thể đạt được điều này? –

3

Nếu bạn không biết loại nào - tức là nó có thể là bất kỳ loại nào - thì tùy chọn tốt nhất là sử dụng sự phản chiếu. Đó là một giải pháp tốt. Tại sao bạn cảm thấy nó không phải là một giải pháp tốt?

1

Ví dụ này lưu trữ một phương pháp In cho bạn cho hiệu suất sử dụng Expressions:

class MyObject 
    { 
     public int Property1 { get; set;} 
     public string Property2 { get; set; } 
    } 
    class MyOtherObject 
    { 
     public int Property3 { get; set; } 
     public string Property4 { get; set; } 
    } 
    static void Main(string[] args) 
    { 
     Array.ForEach(new[] 
     { 
      new MyObject { Property1 = 1, Property2 = "P" }, 
      new MyObject { Property1 = 2, Property2 = "Q" } 
     }, Print); 
     Array.ForEach(new[] 
     { 
      new MyOtherObject { Property3 = 3, Property4 = "R" }, 
      new MyOtherObject { Property3 = 4, Property4 = "S" } 
     }, Print); 
     Console.ReadKey(); 
    } 
    static void Print<T>(T item) 
    { 
     ObjectPrinter<T>.PrintAction(item, Console.Out); 
    } 
    static class ObjectPrinter<T> 
    { 
     public static readonly Action<T, TextWriter> PrintAction = CreatePrintAction(); 
     private static Action<T, TextWriter> CreatePrintAction() 
     { 
      ParameterExpression item = Expression.Parameter(typeof(T), "item"); 
      ParameterExpression writer = Expression.Parameter(typeof(TextWriter), "writer"); 
      var writeLineMethod = typeof(TextWriter).GetMethod("WriteLine", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string) }, null); 
      var concatMethod = typeof(string).GetMethod("Concat", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(object), typeof(object) }, null); 
      var writeDashedLine = Expression.Call(
       writer, 
       writeLineMethod, 
       Expression.Constant(
        new String('-', 50) 
       ) 
      ); 
      var lambda = Expression.Lambda<Action<T, TextWriter>>(
       Expression.Block(
        writeDashedLine, 
        Expression.Block(
         from property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance) 
         where property.GetGetMethod().GetParameters().Length == 0 
         select Expression.Call(
          writer, 
          writeLineMethod, 
          Expression.Call(
           null, 
           concatMethod, 
           Expression.Constant(
            property.Name + ":" 
           ), 
           Expression.Convert(
            Expression.Property(
             item, 
             property 
            ), 
            typeof(object) 
           ) 
          ) 
         ) 
        ), 
        writeDashedLine 
       ), 
       item, 
       writer 
      ); 
      return lambda.Compile(); 
     } 
    } 
Các vấn đề liên quan