使用lambda表达式和泛型编写此方法的更好方法

时间:2011-08-31 13:25:56

标签: generics lambda c#-3.0 extension-methods

我需要一种从IDataRecord检索数据的方法,而不使用恼人的“魔术字符串”来表示字段名称。 经过一番研究后,我提出了这种扩展方法:

public static T2 ReadValue<T1, T2>(this IDataRecord record, Expression<Func<T1, T2>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    string fieldName = body.Member.Name;
    int ordinal = record.GetOrdinal(fieldName);
    return (T2)(record.IsDBNull(ordinal) ? default(T2) : record.GetValue(ordinal));
}

然后我就这样使用这个方法:

product.Name = record.ReadValue<Product, string>(p => p.Name);

还有另一种简化此方法的方法吗?我喜欢这种行为,但不喜欢这种风格! :)

非常感谢你!

2 个答案:

答案 0 :(得分:2)

也许这会奏效(我没有测试过):

public static void ReadValue<T1, T2>(this IDataRecord record, T1 product, Expression<Func<T1, T2>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    string fieldName = body.Member.Name;
    int ordinal = record.GetOrdinal(fieldName);
    var val = (T2)(record.IsDBNull(ordinal) ? default(T2) : record.GetValue(ordinal));
    ((PropertyInfo)body.Member).SetValue(product, val, null);
} 

然后你可以这样称呼它:

record.ReadValue(product, p => p.Name); 

名称仅指定一次,编译器推断通用类型。显然T1需要是参考类型。

答案 1 :(得分:2)

问题是您可以从属性中推断出string结果类型,但您仍然必须在类型参数中指定它,因为您还必须指定Product

解决这个问题的好方法是输入数据记录:

IDataRecord<Product> productRecord = ...;

string name = productRecord.ReadValue(p => p.Name);

这似乎是可行的,因为在您所暗示的ORM上下文中,您应该知道记录所代表的数据类型。

困难的部分是上面代码中的...。它需要一些基础设施,但你只需要编写一次就可以在任何地方使用它。第一步是派生类型化数据记录:

public interface IDataRecord<T> : IDataRecord
{
    TValue GetValue<TValue>(Expression<Func<T, TValue>> getter);
}

接下来,使用Decorator pattern实现类型化数据记录(枯燥但直截了当):

public class DataRecord<T> : IDataRecord<T>
{
    private readonly IDataRecord _untypedRecord;

    public DataRecord(IDataRecord untypedRecord)
    {
        _untypedRecord = untypedRecord;
    }

    public TValue GetValue<TValue>(Expression<Func<T, TValue>> getter)
    {
        ...the original code...
    }

    ...pass through all other members to the untyped record...
}

最后,添加从无类型到记录的转换:

public static class TypedDataRecords
{
    public static IDataRecord<T> TypedAs<T>(this IDataRecord untypedRecord)
    {
        return new DataRecord<T>(untypedRecord);
    }
}

现在,示例如下所示:

IDataRecord<Product> productRecord = record.TypedAs<Product>();

string name = productRecord.ReadValue(p => p.Name);
decimal price = productRecord.ReadValue(p => p.Price);
...