How to use Field<T> with Type?
Asked Answered
T

3

6

I have a method that determines the min and max of a column in a DataTable:

public void GetMinMaxRange( DataTable data, string valueColumnName )
{
   var min = data.AsEnumerable().Min(m => m.Field<double>(valueColumnName));
   var max = data.AsEnumerable().Max(m => m.Field<double>(valueColumnName));
}

I want to refactor this to be:

public void GetMinMaxRange( DataTable data, string valueColumnName )
{
   DataColumn column = data.Columns[valueColumnName];
   var min = data.AsEnumerable().Min(m => m.Field<column.DataType>(valueColumnName));
   var max = data.AsEnumerable().Max(m => m.Field<column.DataType>(valueColumnName));
}

I need to determine the datatype and use it instead of hard-coding m.Field<double>. How to do this?

UPDATE As to why I want to calculate the difference between the min and the max

    public static double/decimal/int GetMinMaxRange<T>(DataTable data, 
          string valueColumnName) where T : IComparable<T>
    {
        DataColumn column = data.Columns[valueColumnName];
        var min = data.AsEnumerable().Min(m => m.Field<T>(valueColumnName));
        var max = data.AsEnumerable().Max(m => m.Field<T>(valueColumnName)); ;
        return max - min;
    }
Tao answered 12/9, 2011 at 16:11 Comment(5)
Exactly why do you need it? Try to finish that method, you are not using min/max yet.Flaring
@Henk - updated my question. Does that make sense?Tao
You can write that last sample as public static T GetMinMaxRange<T>(...) ... which indeed makes sense but needs to spec T at compiletime.Flaring
@Henk - yes, but I can't seem to subtract min from max in this way.Tao
Which is an entirely different problem...Flaring
B
6

Simply creating this as a generic should work:

public void GetMinMaxRange<T>( DataTable data, string valueColumnName ) 
                                                             where T : IComparable<T>
{
   DataColumn column = data.Columns[valueColumnName];
   var min = data.AsEnumerable().Min(m => m.Field<T>(valueColumnName));
   var max = data.AsEnumerable().Max(m => m.Field<T>(valueColumnName));
}

Which you would then use as:

GetMinMaxRange<MyType>(dataTable, valueColumnName);
Bulbiferous answered 12/9, 2011 at 16:13 Comment(3)
I think they're asking for runtime. This would still be compile time.Printery
And you will need where T : IComparable<T>Flaring
This answers my original question. I'll ask another one for the subtracting part. Thanks.Tao
F
2

If you want to this on runtime, you can't use generics. You can rewrite your method like this:

public void GetMinMaxRangeTest(DataTable data, string valueColumnName)
{
    DataColumn column = data.Columns[valueColumnName];
    var min = data.AsEnumerable().Min(m => Convert.ChangeType(m[valueColumnName], column.DataType));
    var max = data.AsEnumerable().Max(m => Convert.ChangeType(m[valueColumnName], column.DataType));
}

I have tested this on a list, like this:

List<string> num = new List<string>() { 
    "1", "2", "3", "-1", "11", "10", "100"
};
var min = num.AsEnumerable().Min(m => Convert.ChangeType(m, typeof(int)));

and it yields the proper result: -1.

Frae answered 12/9, 2011 at 16:31 Comment(2)
I get 'Object must implement IConvertible.' error for a DataTable rather than a ListTao
What type does m[valueColumnName] have? double, decimal, int and string are all IConvertible so m[valueColumnName] having a value of this type can't be the problemFrae
F
1

You can't do this so simple. column.DataType will be a SqlDbType and since it will only be known at runtime so the compiler can't verify that it is IComparable.

Flaring answered 12/9, 2011 at 16:15 Comment(1)
column.DataType would return System.Type not SqlDbType according to my intellisenseBarbaric

© 2022 - 2024 — McMap. All rights reserved.