问题描述:

Let's say I have to convert something read from the database into an object. I might have something like this:

public class Foo {

int a;

DateTime b;

float c;

public Foo(int a, DateTime b, float c) {

this.a = a;

this.b = b;

this.c = c;

}

internal Foo(DataRow dr) {

this.a = (int) dr["a"];

this.b = (DateTime) dr["b"];

this.c = (float) dr["c"];

}

}

is there something you can do, perhaps with the dynamic keyword, that can remove type checks and cast automatically? Ideally it would only apply to the internal constructor.

网友答案:

You mean like this?

public class Foo {
    dynamic a;
    dynamic b;
    dynamic c;

    public Foo(int a, DateTime b, float c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
    internal Foo(DataRow dr) {
        this.a = dr["a"];
        this.b = dr["b"];
        this.c = dr["c"];
    }
}

Sure, you can do that if you really hate casting that much, and then the compiler stops being your best friend to prevent you from shooting yourself in the foot.

If getting rid of ugly casts is your main concern, an alternative would be using the DataRowExtensions.Field<T> method extension.

Provides strongly-typed access to each of the column values in the specified row.

Usage example:

public class Foo {
    int a;
    DateTime b;
    float c;

    public Foo(int a, DateTime b, float c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
    internal Foo(DataRow dr) {
        this.a = dr.Field<int>("a");
        this.b = dr.Field<DateTime>("b");
        this.c = dr.Field<float>("c");
    }
}
网友答案:

You could implement a dynamic wrapper around the DataRow (warning: untested code)

public class DynamicDataRow : DynamicObject
{
    private DataRow _dataRow;

    public DynamicDataRow(DataRow dataRow)
    {
        if (dataRow == null)
            throw new ArgumentNullException("dataRow");
        this._dataRow = dataRow;
    }

    public DataRow DataRow
    {
        get { return _dataRow; }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = null;
        if (_dataRow.Table.Columns.Contains(binder.Name))
        {
            result = _dataRow[binder.Name];
            return true;
        }
        return false;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_dataRow.Table.Columns.Contains(binder.Name))
        {
            _dataRow[binder.Name] = value;
            return true;
        }
        return false;
    }
}

Then modify your internal constructor as follows:

internal Foo(DynamicDataRow ddr) {
    dynamic dr = ddr;
    this.a = dr.a;
    this.b = dr.b;
    this.c = dr.c;
}

And call it with

var foo = new Foo(new DynamicDataRow(someDataRowObject));

Unlike sstan's answer, you will lose all the benefits of compile time type checking. Upfront that sounds like a pretty bad thing, but in actuality it's not quite that bad because even with the DataRowExtensions.Field<T> extension method, the type check still happens at run-time because there is no way for the compiler to check that the datatype of the column in the database matches the field that you're trying to stuff that information into. So either way there is going to be run-time type checking.

相关阅读:
Top