我创建了一个函数,它接受一个SQL命令并生成输出,然后可以用它来填充类实例的List.代码效果很好.我在这里提供了一个稍微简化的版本,没有异常处理仅供参考 - 如果您想直接解决问题,请跳过此代码.如果你在这里有建议,我会全力以赴.
public ListReturnList () where T : new() { List fdList = new List (); myCommand.CommandText = QueryString; SqlDataReader nwReader = myCommand.ExecuteReader(); Type objectType = typeof (T); FieldInfo[] typeFields = objectType.GetFields(); while (nwReader.Read()) { T obj = new T(); foreach (FieldInfo info in typeFields) { for (int i = 0; i < nwReader.FieldCount; i++) { if (info.Name == nwReader.GetName(i)) { info.SetValue(obj, nwReader[i]); break; } } } fdList.Add(obj); } nwReader.Close(); return fdList; }
正如我所说,这很好用.但是,出于显而易见的原因,我希望能够使用匿名类调用类似的函数.
问题1:似乎我必须在调用此函数的匿名版本时构造一个匿名类实例 - 这是对的吗?一个示例电话是:
.ReturnList(new { ClientID = 1, FirstName = "", LastName = "", Birthdate = DateTime.Today });
问题2:我的ReturnList函数的匿名版本如下.任何人都可以告诉我为什么对info.SetValue的调用什么都不做?它不会返回错误或任何内容,但也不会更改目标字段的值.
public ListReturnList (T sample) { List fdList = new List (); myCommand.CommandText = QueryString; SqlDataReader nwReader = myCommand.ExecuteReader(); // Cannot use FieldInfo[] on the type - it finds no fields. var properties = TypeDescriptor.GetProperties(sample); while (nwReader.Read()) { // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem? T obj = (T)FormatterServices.GetUninitializedObject(typeof(T)); foreach (PropertyDescriptor info in properties) { for (int i = 0; i < nwReader.FieldCount; i++) { if (info.Name == nwReader.GetName(i)) { // This loop runs fine but there is no change to obj!! info.SetValue(obj, nwReader[i]); break; } } } fdList.Add(obj); } nwReader.Close(); return fdList; }
有任何想法吗?
注意:当我尝试像上面函数中那样使用FieldInfo数组时,typeFields数组的元素为零(即使objectType显示字段名称 - 奇怪).因此,我使用TypeDescriptor.GetProperties代替.
关于使用反射或匿名类的任何其他提示和指导都适用于此 - 我对C#语言的这个特定角落相对较新.
更新:我要感谢杰森解决这个问题的关键.下面是修改后的代码,它将创建一个匿名类实例列表,从查询中填充每个实例的字段.
public ListReturnList (T sample) { List fdList = new List (); myCommand.CommandText = QueryString; SqlDataReader nwReader = myCommand.ExecuteReader(); var properties = TypeDescriptor.GetProperties(sample); while (nwReader.Read()) { int objIdx = 0; object[] objArray = new object[properties.Count]; foreach (PropertyDescriptor info in properties) objArray[objIdx++] = nwReader[info.Name]; fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray)); } nwReader.Close(); return fdList; }
请注意,已构造查询,并且在先前对此对象的方法的调用中初始化了参数.原始代码具有内部/外部循环组合,因此用户可以在其匿名类中具有与字段不匹配的字段.但是,为了简化设计,我决定不允许这样做,而是采用了Jason推荐的db字段访问.另外,感谢Dave Markle帮助我更多地了解使用Activator.CreateObject()与GenUninitializedObject的权衡.
匿名类型封装了一组只读属性.这解释了
为什么Type.GetFields
在匿名类型上调用时返回一个空数组:匿名类型没有公共字段.
匿名类型的公共属性是只读的,不能通过调用设置其值PropertyInfo.SetValue
.如果您PropertyInfo.GetSetMethod
以匿名方式致电物业,您将收到回复null
.
事实上,如果你改变了
var properties = TypeDescriptor.GetProperties(sample); while (nwReader.Read()) { // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem? T obj = (T)FormatterServices.GetUninitializedObject(typeof(T)); foreach (PropertyDescriptor info in properties) { for (int i = 0; i < nwReader.FieldCount; i++) { if (info.Name == nwReader.GetName(i)) { // This loop runs fine but there is no change to obj!! info.SetValue(obj, nwReader[i]); break; } } } fdList.Add(obj); }
至
PropertyInfo[] properties = sample.GetType().GetProperties(); while (nwReader.Read()) { // No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem? T obj = (T)FormatterServices.GetUninitializedObject(typeof(T)); foreach (PropertyInfo info in properties) { for (int i = 0; i < nwReader.FieldCount; i++) { if (info.Name == nwReader.GetName(i)) { // This loop will throw an exception as PropertyInfo.GetSetMethod fails info.SetValue(obj, nwReader[i], null); break; } } } fdList.Add(obj); }
您将收到一个异常,通知您无法找到属性集方法.
现在,为了解决您的问题,您可以做的就是使用Activator.CreateInstance
.对不起,我懒得为你输入代码,但下面将演示如何使用它.
var car = new { Make = "Honda", Model = "Civic", Year = 2008 }; var anothercar = Activator.CreateInstance(car.GetType(), new object[] { "Ford", "Focus", 2005 });
因此,只需运行一个循环,就像你已经完成的那样,填充你需要传递给的对象数组,Activator.CreateInstance
然后Activator.CreateInstance
在循环完成时调用.属性顺序在这里很重要,因为两个匿名类型是相同的,当且仅当它们具有相同数量的具有相同类型和相同名称的相同顺序的属性时.
有关更多信息,请参阅匿名类型的MSDN页面.
最后,这真的是一个旁边,与你的问题没有密切关系,但以下代码
foreach (PropertyDescriptor info in properties) { for (int i = 0; i < nwReader.FieldCount; i++) { if (info.Name == nwReader.GetName(i)) { // This loop runs fine but there is no change to obj!! info.SetValue(obj, nwReader[i]); break; } } }
可以简化
foreach (PropertyDescriptor info in properties) { info.SetValue(obj, nwReader[info.Name]); }