Using Reflection to create a DataTable from a Class?

I've just learned about Generics and I'm wondering whether I can use it to dynamically build datatables from my classes.

Or I might be missing the point here. Here is my code, what I'm trying to do is create a datatable from my existing class and populate it. However I'm getting stuck in my thought process.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace Generics
{
    public class Dog
    {
        public string Breed { get; set; }
        public string Name { get; set; }
        public int legs { get; set; }
        public bool tail { get; set; }
    }

    class Program
    {
        public static DataTable CreateDataTable(Type animaltype)
        {
            DataTable return_Datatable = new DataTable();
            foreach (PropertyInfo info in animaltype.GetProperties())
            {
                return_Datatable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
            return return_Datatable;
        }

        static void Main(string[] args)
        {
            Dog Killer = new Dog();
            Killer.Breed = "Maltese Poodle";
            Killer.legs = 3;
            Killer.tail = false;
            Killer.Name = "Killer";

            DataTable dogTable = new DataTable();
            dogTable = CreateDataTable(Dog);
//How do I continue from here?


        }      
    }
}    

Now At the DataTable point it errors. Also, being new to reflection and Generics, how will I actually populate the data with the Killer class?


Building up on all the previous answers, here is a version that creates a DataTable from any collection:

public static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
    Type type = typeof(T);
    var properties = type.GetProperties();      
    
    DataTable dataTable = new DataTable();
    dataTable.TableName = typeof(T).FullName;
    foreach (PropertyInfo info in properties)
    {
        dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
    }
    
    foreach (T entity in list)
    {
        object[] values = new object[properties.Length];
        for (int i = 0; i < properties.Length; i++)
        {
            values[i] = properties[i].GetValue(entity);
        }
        
        dataTable.Rows.Add(values);
    }
    
    return dataTable;
}

Here is a more compact version of David's answer that is also an extension function. I've posted the code in a C# project on Github.

public static class Extensions
{
    public static DataTable ToDataTable<T>(this IEnumerable<T> self)
    {
        var properties = typeof(T).GetProperties();

        var dataTable = new DataTable();
        foreach (var info in properties)
            dataTable.Columns.Add(info.Name, Nullable.GetUnderlyingType(info.PropertyType) 
               ?? info.PropertyType);

        foreach (var entity in self)
            dataTable.Rows.Add(properties.Select(p => p.GetValue(entity)).ToArray());

        return dataTable;
    }     
}

I have found that this works very well in conjunction with code to write a DataTable to CSV.


my favorite homemade function. it create and populate all at same time. throw any object.

 public static DataTable ObjectToData(object o)
 {
    DataTable dt = new DataTable("OutputData");

    DataRow dr = dt.NewRow();
    dt.Rows.Add(dr);

    o.GetType().GetProperties().ToList().ForEach(f =>
    {
        try
        {
            f.GetValue(o, null);
            dt.Columns.Add(f.Name, f.PropertyType);
            dt.Rows[0][f.Name] = f.GetValue(o, null);
        }
        catch { }
    });
    return dt;
 }

The error can be resolved by changing this:

dogTable = CreateDataTable(Dog);

to this:

dogTable = CreateDataTable(typeof(Dog));

But there are some caveats with what you're trying to do. First, a DataTable can't store complex types, so if Dog has an instance of Cat on it, you won't be able to add that as a column. It's up to you what you want to do in that case, but keep it in mind.

Second, I would recommend that the only time you use a DataTable is when you're building code that knows nothing about the data its consuming. There are valid use cases for this (e.g. a user-driven data mining tool). If you already have the data in the Dog instance, just use it.

Another little tidbit, this:

DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);

can be condensed to this:

DataTable dogTable = CreateDataTable(Dog);