2

I have a couple of classes in c# console application.

public class Cars:List<Car>
{}

and:

public class Drivers:List<Driver>
{}

The goal is to render console-output always the same manner, doesn't matter if cars or drivers are parsed to rendering-method. So the parameter is of type object. Of course the properties may have different names.

The output should look like this:

// Cars
Brand HP  Topspeed
VW    100 200
VW    90  150

or

// Drivers
Name Age Sex 
Pit  18  m
Jane 20  f

So I need to read property-names to make them column headers and then read values for each object. I've already searched a while and always come things like this:

public static void RenderOutput(object obj)
{
    foreach (var prop in obj.GetType().GetProperties())
    {
    }
}

Although at runtime mouseover obj shows all the desired objects and properties (Brand, HP, VW, 100, ...) very well, I do not manage to squeeze the values out of prop item car by car or driver by driver.

Any suggestion? Thx jimbo2015

Arghya C
  • 9,805
  • 2
  • 47
  • 66
Jimbo2015
  • 21
  • 2

3 Answers3

2

I would suggest going to interface route, have an interface which will have method RenderOutput. Both cars and drivers will implement the interface, and provide their own implementations of the method. That will be more appropriate as cars method will not know about driver or visa-verse. Where you are calling the method, instead of passing the object, you call the instance method.

Adarsha
  • 2,267
  • 22
  • 29
1

You should use BindingFlags when using reflection in order to get the properties of an instance. for example to get all the private properties of an object o you should use:

IEnumerable<PropertyInfo> props = o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (PropertyInfo info in props)
{
    Console.WriteLine(info.Name + info.GetValue(o));
}

After saying that, notice that having an interface which will be implemented by Car and Driver for the printing method is probably more correct design-wise. If you want to use reflection anyway I would recomend adding Attributes over the properties you want to print, with the desired display name in order to separete the display from the code's naming conventions.

Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
1

The correct approach would be implement an interface (as @Adarsha has said) and implement RenderOutput() method in each class to have it's own specific implementation with correct list of properties, correct formatting etc.

Now, if you still want to stick to your approach, this is will work.

Let's assume these are your classes

public class Car
{
    public string Brand { get; set; }
    public int HorsePower { get; set; }
    public int TopSpeed { get; set; }
}

public class Driver
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Sex { get; set; }
}

Now, these methods will use reflection to read the properties and writethem out in console.

public static void RenderOutput(IEnumerable<object> collection)
{
    RenderHeader(collection.First()); //collection not validated
    collection.ToList().ForEach(item => RenderBody(item));
}

private static void RenderHeader(object obj)
{
    var properties = obj.GetType().GetProperties().OrderBy(p => p.Name);
    Console.WriteLine("");
    foreach (var prop in properties)
    {
        Console.Write(prop.Name + "\t"); //or ("{0,15}", prop.Name)
    }
}

private static void RenderBody(object obj)
{
    var properties = obj.GetType().GetProperties().OrderBy(p => p.Name);
    Console.WriteLine("");
    foreach (var prop in properties)
    {
        Console.Write((prop.GetValue(obj, null)) + "\t"); //or ("{0,15}", (prop.GetValue(obj, null)))
    }
}

You can now call the RenderOutput() method on any IEnumerable<T> to get your desired output. You can test it like this

static void Main(string[] args)
{
    //test data
    var cars = new List<Car>();
    cars.Add(new Car { Brand = "BMW", HorsePower = 100, TopSpeed = 200 });
    cars.Add(new Car { Brand = "VW", HorsePower = 90, TopSpeed = 150 });
    var drivers = new List<Driver>();
    drivers.Add(new Driver { Name = "Prit", Age = 18, Sex = "Male" });
    drivers.Add(new Driver { Name = "Jane", Age = 20, Sex = "Female" });

    RenderOutput(cars);
    Console.WriteLine();
    RenderOutput(drivers);

    Console.ReadLine();
}

This is the generated output:

Brand   HorsePower      TopSpeed
BMW     100     200
VW      90      150

Age     Name    Sex
18      Prit    Male
20      Jane    Female

Note: This will not work for complex type properties!

Arghya C
  • 9,805
  • 2
  • 47
  • 66