1

Suppose I have the following class:

public class MyClass
{
    public decimal myDecimal;
    public string myString;
}

I want to use the DataRowExtensions method Field<>

Currently, I am using the class like so:

MyClass myClass = new MyClass();
myClass.myDecimal = row.Field<decimal>("MyDecimalColumnName");
myClass.myString = row.Field<string>("MyStringColumnName");

However, if I ever decide to change the type of myDecimal to something other than decimal, I want the call to row.Field to reflect the correct data.

I want something similar to the following syntax:

myClass.myDecimal = row.Field<typeof(myClass.myDecimal)>("MyDecimalColumnName");

This doesn't compile, and I have no idea how to use typeof or GetType() to just return decimal, whatever that would be called.

Is there a way to do this, or something similar? I figured this could accomplished at compile time as the types are already known, and since generics are compile time constructs.

Thanks!

Matthew
  • 24,703
  • 9
  • 76
  • 110

2 Answers2

5

First, note that public fields are usually a very bad idea; but if we assume this was a private backing field, there are 2 interesting options here;

The first is to exploit generic type inference; this doesn't work for the return type, but does for the parameters, so you could have:

row.GetField("MyDecimalColumnName", out obj.someField);

where that is:

GetField<T>(string name, out T value);

Another trick would be to use implicit operators, i.e. have GetField(string) return a dummy object that has implicit conversion operators to a few types such as int, decimal, etc, and do the work / conversion in the operator. A bit hacky, but would work - syntax would be:

myClass.myDecimal = row.Field("SomeColumn")

with:

SomeDummyType GetField(string name);

and with SomeDummyType having an implicit static conversion operator or several.

However! IMO the best option here is to use a tool such as an ORM or micro-ORM to load the values for you, and don't use DataRow at all.

Another simple option is just to go old-school:

myClass.myDecimal = (decimal)row["SomeColumn"];

I mean - is the .Field<T> really helping you all that much? Do you genuinely refactor your classes often enough that this is worth worrying about it?

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • I like your `row.GetField` suggestion, though I'm wondering why I can't explicitly set the type of return values. As for using `Field`, I use it because it helps using nullable types (the DataRow returns `DBNull.Value` for nullable columns, and `null` for columns that are non-existant). For example `decimal? x = (decimal?)row["MyColumn"]` will fail if MyColumn is null, but will NOT fail if MyColumn is non-existant. – Matthew Mar 13 '12 at 14:43
  • 1
    @Matthew yes, don't get me started on that craziness; I am not a fan of `DBNull.Value` – Marc Gravell Mar 13 '12 at 14:46
  • @Matthew of course it won't work quite as conveniently when you've fixed your code by getting rid of those public fields. Hint. Hint Hint. Hint. – Marc Gravell Mar 13 '12 at 14:50
  • Yea I know using public fields is generally bad form, but these objects are just POCO. – Matthew Mar 13 '12 at 14:55
1

You could use reflection to dynamically construct the proper generic method:

MyClass myClass = new MyClass();
var myType = typeof(MyClass.myDecimal);
var myMethod = Type.GetType("System.Data.DataRowExtensions")
   .GetMethod("Field", new[]{typeof(DataRow), typeof(string));
var genericMethod = myMethod.MakeGenericMethod(new[]{myType});

myClass.myDecimal = genericMethod.Invoke(row, new object[]{"MyDecimalColumnName"});
KeithS
  • 70,210
  • 21
  • 112
  • 164