在 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));
}
问题在于,您可能知道,类 DataRow
的 Field
方法的扩展不接受变量,需要显式填充。
我在 C# 方面没有经验,所以我想提出以下两个问题:
- 我想要做的事情是好的做法吗?
- 如何填写正确的
Field<extension>(name)
分名?
您需要获取泛型方法的方法信息,并对其调用 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];
}