views:

225

answers:

3

Is there any better way than the following?

Particularly, I want to replace Activator with something else.

public static List<T> ToList<T>(DataTable dt)
        {
            Type type = typeof(T);

            List<T> list = new List<T>();

            foreach (DataRow dr in dt.Rows)
            {
                object[] args = new object[1];

                args[0] = dr;

                list.Add((T)Activator.CreateInstance(type, args));
            }

            return list;
        }
+2  A: 

I don't really see any way to improve this code - why do you want to avoid Activator?

One option you could explore would be to create some sort of interface like this:

interface IFoo
{
    void Initialize(DataRow dr);
}

And then implement this interface on any type that gets passed to this method. Then you would constrain your generic type parameter like this:

public static List<T> ToList<T>(DataTable dt)
    where T : IFoo, new()

Then change the implementation of your method like this:

public static List<T> ToList<T>(DataTable dt)
    where T : IFoo, new()
{
    List<T> list = new List<T>();

    foreach (DataRow dr in dt.Rows)
    {
        T t = new T();
        t.Initialize(dr);
        list.Add(t);
    }
    return list;
}
Andrew Hare
+5  A: 

The first thing I want to mention is that you probably don't need a list. Odds are, an IEnumerable is enough. Even if you do need a list, it's trivial to convert an IEnumerable to a list.

With that in mind, this code is a nice generic way to accomplish it:

public static IEnumerable<T> ToEnumerable<T>(DataTable dt, Func<DataRow, T> translator)
{
    foreach(DataRow dr in dt.Rows)
    {
       yield return translator(dr);
    }
}

Hopefully you can see how re-usable this is. All you need to do is supply a function that knows how to convert an individual DataRow into your T type. That function might use Activator, but it doesn't have to. It might just use a normal constructor and set a few properties.

Joel Coehoorn
+1, using yield you'll call your translator just as you need your `IEnumerable<T> `items; if you're using some logic to abort your list processing then you'll save some processor cycles.
Rubens Farias
But the problem with this approach is, it is not backward compatible with .net 2.0.
JMSA
You can do with .NET 2.0, you just have to use an anonymous delegate instead of a Func<DataRow,T> to perform the conversion. Or you can even just write it without the delegate and map to your resulting class type directly and just use new()
Chris Patterson
A: 

One thing I'd add to Andrew's answer is if you go that route you can (sorta) avoid the Activator class by constraining the generic method with a new() constraint.

public static List<T> ToList<T>(DataTable dt)
    where T : IFoo, new()
{
    ...
    foreach ( ... ) {
       var foo = new T();
       foo.Initialize(dataRow);
       list.Add(foo);
    }
    ...
}

The reason I say "sorta" is because C# actually just compiles that into an Activator.CreateInstance call at compile-time anyway. But it looks much cleaner.

Josh Einstein
Doh, looks like Andrew just added that to his answer too.
Josh Einstein