自己动手之使用反射和泛型,动态读取XML创建类实例并赋值
回到目录
前言:最近小匹夫参与的游戏项目到了需要读取数据的阶段了,那么觉得自己业余时间也该实践下数据相关的内容。那么从哪入手呢?因为用的是Unity3d的游戏引擎,思来想去就选择了C#读取XML文件这个小功能。网上的例子倒也不少,但总是觉得缺点什么。比如读取xml文件之后该如何处理?看到的文章基本上都是手动创建一个目标类的实例,然后手动从读取的XML文件的内容中给刚才创建的目标类实例相关字段赋值。缺点什么呢?对嘞,感觉上不够简单和智能。 正所谓驱动科技发展的原因就是懒,为了使我们的小工具能够傻瓜到只需要指定一个需要的目标类型和要读取的xml的地址就能实现目标类实例的动态生成,下面的文字就诞生了。 需要解决的问题:问,从xml文件到需要的目标类实例需要几步? 答,读取XML文件,实例化一个目标实例,赋值。
回到目录
问题一:如何读取XML文件所以第一个问题就是如何读取XML文件,参考这篇博客《c#读取XML》,我们可知备选答案无非如下几种:
1.XmlDocument的使用: //XmlDocument使用 XmlDocument doc = new XmlDocument(); doc.Load("./Assets/xml-to-egg/xml-to-egg-test/Test.xml"); 但是要注意的是,XmlDocument是读取整个XML的,所以如果XML内容过多,则会消费很多内存。所以XML内容过大时,不推荐使用XmlDocument。 2.XmlTextReader的使用: XmlTestReader的使用方法 XmlTextReader reader = new XmlTextReader("); 使用read()方法向下读取 while (reader.Read()) { ..... } 要说明与XmlDocument的最大区别,其实也很简单,XmlReader使用Steam(流)来读取文件,所以不会对内存造成太大的消耗。XmlReader通过read()方法不断向下读取,我们就可以在这个过程中进行我们需要的操作。不过这个也不是我们的答案,我们选择的答案在下面。 3.Linq to Xml 在System.Xml.Linq命名空间中,操作十分简单和方便。 Linq to Xml的使用
XElement xml = XElement.Load(读取的xml文件的元素都在生成的XElement的实例xml.Elements中。
string name = xml.Element(name").Value;
......
可见十分简单明了。传入xml文件的路径就会返回一个XElement类型的实例,并且xml文件的元素也都存入了XElement实例中。那么我们读取XML文件的任务就交给它了。 读取XML相关逻辑的代码如下: /// <summary> /// Sets the xml path. </summary> public static void SetXmlPath(string p) { path = p; } Loads the XML Files. private static XElement LoadXML() { if(path == null) return null; XElement xml = XElement.Load(path); return xml; }
回到目录
问题二:如何实例化一个目标实例。假设我们并不知道我们的这个动态读取XML创建实例并赋值的小工具要处理的是什么类型的对象,那问题就来了,总不能每一个不同的类都对应一套处理方法吧?那也太不智能且代码太难以复用了。所以这里我们实例化一个目标实例碰到的第一个问题就来了,也就是如何破解目标类型的问题? 答案是使用泛型。 在实例化具体对象的时候,才确定类型,这样就可以避免由于类型不同而导致的代码无法复用的问题。 那么,下面我们的小工具---XMLToEgg就要出场了,对,就是一个处理引用类型的泛型类。 class XmlToEgg<T> where T : class
{
}
可是光解决了实例类型的问题还是差一步啊,差点什么呢?对啊,那就是如何实例化一个泛型目标实例。这也就是我们在实例化一个目标实例时遇到的第二个问题。 答案是使用反射。 那下面继续上代码: <summary> Creates the class initiate. </summary> void CreateInitiate() { Type t = typeof(T); ConstructorInfo ct = t.GetConstructor(System.Type.EmptyTypes); target = (T)ct.Invoke(null); } 当然这里小匹夫假设我们的目标类的构造函数是不需要参数的,如果需要参数也很简单,看官们自己可以查到这里就不赘述了。 好了,到这里我们如何创建一个一开始我们不知道是什么类型,只有到创建的时候才知道是什么东西的类的实例的问题就解决了。(好绕)
回到目录
问题三:如何为创建好的实例中的字段赋值终于来到了我们的终极问题,也是我们最终的目标,实现从XML到目标类实例的最后一步。在问题二的时候已经说了,作为一个可以复用的工具,对处理的目标类型应该有包容性,那么既然连目标类型都不确定,那么目标类型的字段咋能确定呢?所以这个问题的本质其实就是我不知道目标类有啥字段啊。。。(如果你把字段写死,是不是就没有一点扩展性了。。。low爆有木有),那问题连环一个接一个,我既然不知道目标类有啥字段,那我更不可能知道目标类的字段的类型了吧。好,就算我啥都知道,我应该怎么设呢?直接用instance.field = XXX? 图样图森破。 所以问题的本质是明确的:
正所谓“车到山前必有路,答案还是用反射”。只要能解决上面三个小问题,那么最后这一步就算是迈过去了。话不多说,下面上代码: attribute assignment, 由于反射中设置字段值的方法会涉及到赋值的目标类型和当前类型的转化,
所以需要使用Convert.ChangeType进行类型转化
static T ToEgg()
{
if(target != null)
{
target = null;
}
CreateInitiate();
XElement xml = LoadXML();
Type t = target.GetType();
FieldInfo[] fields = t.GetFields();
string fieldName = string.Empty;
foreach(FieldInfo f in fields)
{
fieldName = f.Name;
if(xml.Element(fieldName) != null)
{
f.SetValue(target,Convert.ChangeType(xml.Element(fieldName).Value,f.FieldType));
}
}
return target;
}
所以看代码就很明白了,简单介绍一下:
这样,一个处理动态读取XML创建类实例并赋值的类或者说小工具XMLToEgg就完成了,下面是完整的代码。 XmlToEgg
Created by chenjd
/// http://www.cnblogs.com/murongxiaopifu/
https://github.com/chenjd/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
namespace EggToolkit
{
class
{
string path;
static T target;
static XmlToEgg()
{
}
<summary>
Sets the xml path.
</summary>
string p)
{
path = p;
}
Loads the XML Files.
static XElement LoadXML()
{
null)
null;
XElement xml = XElement.Load(path);
return xml;
}
Creates the class initiate.
void CreateInitiate()
{
Type t = typeof(T);
ConstructorInfo ct = t.GetConstructor(System.Type.EmptyTypes);
target = (T)ct.Invoke(null);
}
由于反射中设置字段值的方法会涉及到赋值的目标类型和当前类型的转化,
所以需要使用Convert.ChangeType进行类型转化
static T ToEgg()
{
null)
{
target = null;
}
CreateInitiate();
XElement xml = LoadXML();
Type t = target.GetType();
FieldInfo[] fields = t.GetFields();
string.Empty;
in fields)
{
fieldName = f.Name;
null)
{
f.SetValue(target,f.FieldType));
}
}
return target;
}
}
}
测试: 完整的项目代码以及使用方法、测试可以从这里获取:XMLToEgg(https://github.com/chenjd/Unity3D_XMLToEgg)装模作样的声明一下:本博文章若非特殊注明皆为原创,若需转载请保留原文链接(http://www.cnblogs.com/murongxiaopifu/p/4175395.html)及作者信息慕容小匹夫
回到目录
更新(之前在游戏蛮牛更新了,忘了在这里同步)
本作品采用 知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权 陈嘉栋(慕容小匹夫),并且,不得用于商业用途。如您有任何疑问或者授权方面的协商,请 给我留言。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |