Есть ли общий метод для повторения и печати значений в неизвестной коллекции?

Скажем, у меня есть такой метод печати:

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

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

Если я передам коллекцию «Автомобили» методу «Печать», он должен распечатать список деталей «Автомобиля», таких как: Марка, Цвет, Цена, Класс и т. Д.

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


person Prince Ashitaka    schedule 25.02.2011    source источник
comment
Лучшее, что вы можете сделать с дженериками, это реализовать некоторый интерфейс IMyToString (при условии, что ToString не переопределяется напрямую), а затем использовать IEnumerable<IMyToString>. Конечно, есть и другие методы, но обычно они полагаются на сопоставление (TypeConverters — это форма сопоставления) или используют RTTI другими способами.   -  person    schedule 25.02.2011


Ответы (4)


Вы можете реализовать 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();
    }
}

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

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

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

person Markus Johnsson    schedule 25.02.2011
comment
Это работает для того, что я ищу, а именно для печати имен свойств Object один раз, а затем для печати значений для каждого элемента в списке. Спасибо! - person Shiva; 11.06.2014

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

person BinaryTox1n    schedule 25.02.2011
comment
+1 за предложение стандартного шаблона полиморфизма. Если классы запечатаны, то можно использовать шаблон посетителя (подробное совпадение с перевернутым шаблоном ;-). - person ; 25.02.2011
comment
А в шаблоне посетителя .net есть не что иное, как методы расширения. - person Pradeep; 25.02.2011
comment
Приведенное выше решение действительно хорошее. Но у меня есть другая проблема. Скажем, например, что если я передам неизвестную коллекцию типа var result = select p from Persons join o in Orders on p.ID = o.PersonID select new { p.Name, o.Name }; Print(result); В этом случае, как я могу этого добиться? - person Prince Ashitaka; 25.02.2011
comment
@Prince, в этом случае вы можете создать структуру для размещения этого типа данных вместо использования анонимного типа и снова переопределить структуру ToString(). Вам не нужно использовать отражение только для этого. Кроме того, анонимным типам автоматически предоставляется метод ToString(), который перечисляет все свойства и соответствующие значения типа. Поскольку и ваш вариант отражения, и это одинаково негибкие, по-прежнему нет причин использовать отражение - person BinaryTox1n; 25.02.2011

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

person Kirk Broadhurst    schedule 25.02.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();
        }
    }
person Double Down    schedule 28.02.2011