2011-02-25 6 views
6

Digamos que tengo un método de impresión de esta manera:¿Existe un método genérico para iterar e imprimir valores en una colección desconocida?

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

Quiero pasar una clase de colección a este método de impresión, que debe imprimir todos los campos como una tabla. Por ejemplo, mi colección de entradas puede ser "Personas" u "Órdenes" o "Automóviles", etc.

Si paso la colección "Cars" al método de impresión, debe imprimir la lista de detalles del "automóvil" como : Marca, color, precio, clase, etc.

No sabré el tipo de la colección hasta el tiempo de ejecución. Intenté y logré una solución usando TypeDescriptors y PropertyDescriptorCollection. Pero, no creo que sea una buena solución. ¿Hay alguna otra forma de lograr esto usando expresiones o genéricos?

+1

lo mejor que se puede hacer con los genéricos es tener todo poner en práctica algunos 'interfaz IMyToString' (suponiendo ToString no se reemplaza directamente) y luego tome 'IEnumerable '. Existen otros métodos, seguro, pero generalmente se basan en un mapeo (TypeConverters es una forma de mapeo) o utilizan el RTTI de otras maneras. –

Respuesta

6

Se podría aplicar Imprimir esta manera:

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(); 
    } 
} 

Simplemente bucles sobre cada propiedad de la clase para imprimir el nombre de la propiedad, a continuación, imprime sobre cada elemento y para cada elemento que imprime los valores de la propiedades.

Argumentaría que debería usar genéricos aquí (a diferencia de las sugerencias en otras respuestas); desea que los elementos de la colección sean de un solo tipo para que pueda imprimir encabezados de tabla.

Para formatear la tabla, puede verificar las respuestas al this question.

+0

Exactamente lo que estaba buscando.Muchas gracias :) –

+0

Esto funciona para lo que estoy buscando, que es imprimir Nombre (s) de Propiedad del Objeto una vez, y luego imprimir valores para cada elemento en la Lista. ¡Gracias! – Shiva

7

Anule el método ToString() del objeto para incluir toda la información que le gustaría mostrar, y luego simplemente llámelo siempre que lo desee. Sin necesidad de expresiones o genéricos.

+1

+1 por sugerir el patrón de polimorfismo estándar. Si las clases están selladas, entonces se puede usar el "patrón de visitante" (coincidencia de patrón invertido detallado ;-); –

+0

Y en .net patrón de visitante no es más que métodos de extensión. – Pradeep

+0

La solución anterior es realmente buena. Pero, tengo otro problema. Digamos, por ejemplo, qué pasaría si paso una colección desconocida como 'var result = select p from Persons join o en Orders on p.ID = o.PersonID select new {p.Name, o.Name};' 'Print (resultado); 'En este caso, ¿cómo puedo lograr esto? –

3

Si no sabe de qué tipo, es decir, podría ser de cualquier tipo, la mejor opción es utilizar la reflexión. Esa es una buena solución. ¿Por qué sientes que no es una buena solución?

1

Este ejemplo cachés un método de impresión para usted para un rendimiento utilizando las expresiones:

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(); 
     } 
    } 
Cuestiones relacionadas