【问题标题】:Using reflection in C# to modify fields in a loop, extension method在C#中使用反射在循环中修改字段,扩展方法
【发布时间】:2012-04-28 13:37:20
【问题描述】:

从数据库中检索数据后,我发现自己这样做是为了从 DataRow(在本例中为 DVD)中的数据创建域对象:

DataRow drDvd = myDataTable.Rows[0];
Dvd myDvd = new Dvd();
myDvd.id = drDvd.Field<long>("id");
myDvd.title = drDvd.Field<string>("title");
myDvd.description = drDvd.Field<string>("description");
myDvd.releaseDate = drDvd.Field<DateTime>("releaseDate");

我很快就感觉到,我在伪代码中一遍又一遍地这样做:

myDvd.field = drDvd.Field<field.type>(field.name);

我想知道我是否可以让它进入一个循环,但是我以前从未使用过反射。我试过的代码是这样的:

Dvd aDvd = new Dvd();
Type t = aDvd.GetType();
FieldInfo[] fields = t.GetFields();
foreach (FieldInfo fi in fields)
{
    fi.SetValue(aDvd, drDvd.Field<fi.FieldType>(fi.Name));
}

问题在于,您可能知道,DataRow 类的 Field 方法的扩展不接受变量,需要明确填写。

我对 C# 的经验不是很丰富,所以我想提出以下两个问题:

  1. 我正在尝试做的事情是好的做法吗?
  2. 如何为Field&lt;extension&gt;(name) 填写正确的扩展名

【问题讨论】:

  • 如果你正在做大量的反射,请考虑使用fast-member:nuget.org/packages/FastMember一定要通过它的单元测试来了解如何使用它。

标签: c# reflection types extension-methods field


【解决方案1】:

您需要获取泛型方法的方法信息,然后调用它。通过这种方式,您可以以编程方式将泛型类型传递给它。我正在使用手机,但它应该看起来像这样:

MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });

GenericMethod.Invoke(aDvd,new Object[]{fi.Name});

【讨论】:

  • 好的,我更新了我的答案——你应该可以从我拥有的代码中得到它。祝你好运!
  • 很高兴听到,我还更新了调用调用以具有正确的方法签名
【解决方案2】:

在没有必要时使用反射通常是一种不好的做法。因为反射方法是在运行时而不是编译时检查的,所以错误代码更难跟踪,因为编译器无法检查错误。

如果我是您,请查看实体框架,因为您基本上是将数据库数据映射到域对象。 http://msdn.microsoft.com/en-us/library/aa697427%28v=vs.80%29.aspx

【讨论】:

  • 感谢您的评论。问题是我是学生,他们在学校没有教过这个。我目前正在做一些大事,其中截止日期很近,所以我现在不能花时间阅读整个页面。不过,我稍后会这样做。
  • 您正在尝试做的事情称为对象关系映射或 ORM。有很多很好的框架可以做到这一点,包括实体框架,尽管如果你以前没有使用过 LINQ-to-SQL 可能会更好。两者都可以在 Visual Studio 中添加为项目,并且有一个设计器来设置它们。两者还允许您直接执行 SQL 查询,因此您可以使用它们提供的对象映射部分以更高效且经过良好测试的方式解决您的问题。肯定比试图正确思考更好地利用时间。
  • 我猜他们没有教你反思,因为它是相当高级的东西。你说你是学生。如果我要评判你的项目,如果我的学生自己发现了反射,我会很兴奋,尽管你在这里使用它的方式告诉我你并不真正理解反射是什么。对于这种东西,你应该使用像实体框架这样的 ORM。仅在绝对必要时才应使用反射。例如,.NET 反射器,它向您显示已编译程序的 C#/MSIL 代码。但是,嘿,这只是我的意见;-)。
【解决方案3】:

这是构建和填充域对象的一种方式

    DataRow drDvd = new DataRow();
    Dvd aDvd = new Dvd();
    Type type = typeof(Dvd);
    foreach (FieldInfo fi in type.GetFields())
    {
        fi.SetValue(aDvd, drDvd[fi.Name]);
    } 

您使用 DataRow.Field 的方法可能是迂回的。在你的情况下,它不适用。

或者,您可以考虑在您的应用程序中使用其中一种实体框架(NHibernate、Microsoft EF 等)。

【讨论】:

  • 这样,您会将我的所有字段值设置为ObjectField 方法允许强类型。我希望 long 出现在 long 字段中...
【解决方案4】:

我会做一个自定义属性。在执行属性时,您会遇到与数据库相同的字段名称。我目前在我当前的应用程序中使用它并且效果很好。它与 Entity SQL 非常相似。

public class SqlMetaAttribute : Attribute
{
    public string ColumnName { get; set; }
}

那么你的课就是这样的

public class Person
{
    [SqlMeta(ColumnName = "First_Name")]
    publice string FirstName { get; set; }

    [SqlMeta(ColumnName = "Last_Name")]
    publice string LastName { get; set; }
}

然后您将拥有一个具有相同功能的辅助类。在这种情况下,我假设外部调用者正在遍历数据表。使用模板 T 使其具有通用性使其真正可重用。而不是仅仅拥有一个“DVD”类型的实现,然后为另一个实现和粘贴。

public static T CreateObjectFromRow<T>(DataRow row)
{
    var newObject = new T();

    if (row != null) SetAllProperties(row, newObject);

    return newObject;
}

public static void SetAllProperties<T>(DataRow row, T newObject)
{
    var properties = typeof(T).GetProperties();

    foreach(var propertyInfo in properties)
    {
        SetPropertyValue(row, newObject, propertyInfo);
    }
}
public static void SetPropertyValue(DataRow row, T newObject, PropertyInfo propertyInfo)
{
    var columnAttribute = propertyInfo.FindAttribute<SqlMetaAttribute>();

    if (columnAttribute == null) return;

    // If the row type is different than the object type and exception will be thrown, but that is
    // okay because if that happens you have to fix your object you are using, or might need some
    // more custom code to help you with that.
    propertyInfo.SetValue(newObject, row.GetValue<object>(columnAttribute.ColumnName), null);
}

// Extension method for row.GetValue<object> used above
public static T GetValue<T>(this DataRow row, string columnName)
{
    if (row.ColumnNameNotFound(columnName) || row.Table.Columns[columnName] == null || row[columnName] is DBNull)
    {
        return default(T);
    }

    return (T)row[columnName];
}

【讨论】:

  • 看起来真的很棒...但是不是字符串的字段呢?它会正确找到longbool 值吗?我们数据库中使用的数据类型是 bit 用于布尔值...
  • 你基本上是通过这样做重新实现实体框架代码。这是“SqlMeta”属性:msdn.microsoft.com/en-us/library/…
猜你喜欢
  • 2021-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
相关资源
最近更新 更多