jamone
jamone

Reputation: 17421

Use generics to make a method work for any data type

I'm not quite sure I'm understanding how I can utilize generics in C# properly. Say I have the following method. I would like to allow it to work on Lists of any type. Currently I have List where Row is a custom struct, I want to reuse this sort method for half a dozen structs that I make. I thought I could just do List<T> in the return type and parameter type but it doesn't like that.

public static List<Row> SortResults( List<Row> listRow, string sortColumn, bool ascending)
        {
            switch (ascending)
            {
                case true:
                    return (from r in listRow
                            orderby r.GetType().GetField(sortColumn).GetValue(r)
                            select r).ToList<Row>();
                case false:
                    return (from r in listRow
                            orderby r.GetType().GetField(sortColumn).GetValue(r) descending
                            select r).ToList<Row>();
                default:
                    return listRow;
            }
        }

Upvotes: 1

Views: 314

Answers (3)

Jon Skeet
Jon Skeet

Reputation: 1502106

Well, here's your original code made generic:

public static List<T> SortResults<T>(List<T> listRow, 
                                     string sortColumn, bool ascending)
{
    switch (ascending)
    {
        case true:
            return (from r in listRow
                    orderby r.GetType().GetField(sortColumn).GetValue(r)
                    select r).ToList();
        case false:
            return (from r in listRow
                    orderby r.GetType().GetField(sortColumn).GetValue(r) descending
                    select r).ToList();
        default:
            return listRow;
    }
}

Note how I've removed the type argument from the ToList calls - let the compiler work it out :)

(As an aside, I don't know why the compiler requires a default case here. Maybe it just always assumes there will be unlisted potential values.)

However, this can be made nicer:

public static List<T> SortResults<T>(List<T> listRow, 
                                     string sortColumn,
                                     bool ascending)
{
    FieldInfo field = typeof(T).GetField(sortColumn);
    Func<T, object> projection = t => field.GetValue(t);
    IEnumerable<T> sequence = ascending ? listRow.OrderBy(projection)
        : listRow.OrderByDescending(projection);
    return sequence.ToList();
}

This is still fairly inefficient (as it's using reflection) but at least it's not getting the FieldInfo separately for each item. If you want better performance, you can have a generic helper type which caches a delegate mapping a value to the field's value for each field, and then fetch that delegate once at the start of the method. It would definitely be more work, but I'd expect it to be an order of magnitude faster.

Upvotes: 8

Randy Minder
Randy Minder

Reputation: 48482

public static List<T> SortResults( List<T> listObject, string sortColumn, bool ascending) where T: class

Then your ToList would be ToList<T>();

T represents your entity/object type.

Upvotes: 2

itowlson
itowlson

Reputation: 74822

You need to make the method itself generic:

public static List<Row> SortResults<Row>( List<Row> listRow, ...
                                   ^^^^^

Upvotes: 1

Related Questions