Есть ли общий метод для итерации и печати значений в неизвестной коллекции? - PullRequest
6 голосов
/ 25 февраля 2011

Допустим, у меня есть метод Print, подобный этому:

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

Я хочу передать класс коллекции этому методу Print, который должен печатать все поля как таблица. Например, моя коллекция входных данных может быть «Персоны» или «Заказы» или «Автомобили» и т. Д.

Если я передам коллекцию "Cars" методу Print, он должен напечатать список деталей "Car", таких как: Марка, Цвет, Цена, Класс и т. Д.

Я не буду знать тип коллекции до времени выполнения. Я попытался и достиг решения, используя TypeDescriptors и PropertyDescriptorCollection. Но я не думаю, что это хорошее решение. Есть ли другой способ добиться этого с помощью выражений или обобщений?

Ответы [ 4 ]

7 голосов
/ 25 февраля 2011

Переопределите метод ToString() объекта, включив в него всю информацию, которую вы хотите отобразить, а затем просто вызывайте его, когда захотите. Нет необходимости в выражениях или обобщениях.

6 голосов
/ 25 февраля 2011

Вы можете реализовать Print следующим образом:

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

Он просто зацикливается на каждом свойстве класса, чтобы напечатать имя свойства, затем печатает на каждом элементе и для каждого элемента печатает значениясвойства.

Я бы сказал, что вы должны использовать дженерики здесь (в отличие от предложений в других ответах);Вы хотите, чтобы элементы в коллекции были одного типа, чтобы вы могли печатать заголовки таблиц.

Для форматирования таблицы вы можете проверить ответы на этот вопрос .

3 голосов
/ 25 февраля 2011

Если вы не знаете, какой тип - то есть это может быть любой тип - тогда лучшим вариантом будет использование отражения.Это хорошее решение.Почему вы считаете, что это не очень хорошее решение?

1 голос
/ 01 марта 2011

В этом примере кешируется метод Print для производительности с использованием выражений:

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