在 C# 中使用反射修改循环、扩展方法中的字段

本文关键字:扩展 方法 字段 循环 修改 反射 | 更新日期: 2023-09-27 18:32:37

从数据库中检索数据后,我发现自己这样做是为了从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));
}

问题在于,您可能知道,类 DataRowField 方法的扩展不接受变量,需要显式填充。

我在 C# 方面没有经验,所以我想提出以下两个问题:

  1. 我想要做的事情是好的做法吗?
  2. 如何填写正确的Field<extension>(name)分名

在 C# 中使用反射修改循环、扩展方法中的字段

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

MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });
GenericMethod.Invoke(aDvd,new Object[]{fi.Name});

当不是真正必要的时候,使用反射通常是一种不好的做法。由于反射方法是在运行时而不是编译时检查的,因此更难跟踪错误的代码,因为编译器无法检查错误。

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

这是构造和填充域对象的方法之一

    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等)。

我会做一个自定义属性。 在执行属性时,您会遇到字段名称与数据库相同的问题。 我目前在我当前的应用程序中使用它,它工作得很好。 它与实体 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];
}