C#中使用反射无法将列表进行强制转换

4
我目前在C#中使用反射遇到了很多麻烦。我正在编写的应用程序允许用户使用配置文件修改某些对象的属性。我希望能够将对象模型(用户项目)保存为XML。以下函数在foreach循环的中间被调用,循环遍历包含项目中所有其他对象的对象列表。想法是通过递归地将对象模型转换为XML。

不要担心对“Unreal”的调用,只需在对象名称中包含特定单词时轻微修改对象名称即可。

      private void ReflectToXML(object anObject, XmlElement parentElement)
  {
     Type aType = anObject.GetType();
     XmlElement anXmlElement = m_xml.CreateElement(Unreal(aType.Name));
     parentElement.AppendChild(anXmlElement);
     PropertyInfo[] pinfos = aType.GetProperties();
     //loop through this objects public attributes
     foreach (PropertyInfo aInfo in pinfos)
     {
        //if the attribute is a list
        Type propertyType = aInfo.PropertyType;
        if ((propertyType.IsGenericType)&&(propertyType.GetGenericTypeDefinition() == typeof(List<>)))
        {
           List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
           foreach (object aListObject in listObjects)
           {
              ReflectToXML(aListObject, anXmlElement);
           }
        }
        //attribute is not a list
        else
           anXmlElement.SetAttribute(aInfo.Name, "");
     }
  }

如果对象的属性只是字符串,则应将其作为字符串属性写入XML中。如果对象的属性是列表,则应递归调用“ReflectToXML”,将自身作为参数传递,从而创建我所需的嵌套结构,以很好地反映内存中的对象模型。
我的问题出在这行代码上。
List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);

这个代码片段可能存在问题,因为它返回了空值(null)。 在调试过程中,我修改了这一行:

object temp = aInfo.GetValue(anObject,null);

我在它上面设置了一个断点,以查看“GetValue”返回的内容。它返回一个“对象的通用列表”。 我肯定应该能够强制转换吧?让人恼火的是,temp变成了一个对象的通用列表,但由于我将temp声明为单个对象,所以无法循环遍历它,因为它没有枚举器。

当我只有它作为类的propertyInfo时,如何循环遍历对象列表?

我知道此时我只会保存一组空字符串,但没关系。现在我很高兴看到结构体被保存下来。

谢谢提前

4个回答

5
我假设实际值不是List<object>,而是像List<string>List<int>这样的其他类型,它们并非完全是object
如果是这样的话,那么强制转换失败的原因是泛型类既不是协变也不是逆变。
然而,在C# 4.0中,您将能够通过转换为IEnumerable<object>使foreach循环工作,因为接口可以是协变/逆变的。
更多信息请参见: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
编辑:
想一想,你在这里不需要泛型的variance。 List<T>实现了非泛型的IEnumerable。这就是foreach循环所需的全部内容,您只需要将其强制转换为IEnumerable而不是List<object>,然后一切都应该正常工作。

这将是一个用户自定义对象列表。(这些是顶级类下唯一的列表)我使用“对象”的原因是它可以是包含不同对象类型的许多不同列表中的任何一个。如果我可以循环遍历该列表并将对象传回相同的函数,那么它应该通过“aType.Name”找出其名称并将其写入XML。不幸的是,我只能使用C# express 2008。 - DrLazer

4

泛型和反射混合使用时,特别是在列表中,效果不佳。我强烈建议使用(非泛型)IList,如果失败了(一些泛型列表没有实现它),则使用 IEnumerable(因为 IEnumerable<T> : IEnumerable),并手动调用 Add

我真的能感受到你的痛苦(我维护一个开源序列化 API)。


谢谢,这让我开始思考其他解决问题的方法。我将列表切换为ArrayLists,现在一切都很好。 - DrLazer
@DrLazer - 说实话,你甚至不应该需要那个;因为List<>ArrayList都实现了IList,与IList接口交互,具体列表类型并不重要 - Marc Gravell

1

很遗憾,我只能使用C# express 2008。 - DrLazer
@Kevin - 列表接口没有变异性,因为它们既不是“in”也不是“out”。 - Marc Gravell
@Marc,是的,我想表达的是,在接口方面,总体上方差在3.0和4.0之间有所变化,尽管这种变化不一定适用于此情况。 - kemiller2002

0

你不能使用OfType()将它们转换为对象吗?

List<object> listObjects = anObject.OfType<object>().ToList();

更新:

如果需求不是 List<object> 的话,这个方案也可以完美解决问题,并且不会出现列表项重复的情况:

var enumerableObjects = anObject.OfType<object>();

我的PropertyInfo类似乎没有OfType方法。我正在使用C# Express 2008。 - DrLazer
您需要引用System.Data.Linq.dll并添加using System.Linq;。 - Nick Gotch
1
我不想说,但这是一种非常浪费的方法。仅仅为了读取列表而复制它?糟糕。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接