自己动手之使用反射和泛型,动态读取XML创建类实例并赋值
前言:
最近小匹夫參與的游戲項目到了需要讀取數(shù)據(jù)的階段了,那么覺得自己業(yè)余時間也該實踐下數(shù)據(jù)相關(guān)的內(nèi)容。那么從哪入手呢?因為用的是Unity3d的游戲引擎,思來想去就選擇了C#讀取XML文件這個小功能。網(wǎng)上的例子倒也不少,但總是覺得缺點什么。比如讀取xml文件之后該如何處理?看到的文章基本上都是手動創(chuàng)建一個目標(biāo)類的實例,然后手動從讀取的XML文件的內(nèi)容中給剛才創(chuàng)建的目標(biāo)類實例相關(guān)字段賦值。缺點什么呢?對嘞,感覺上不夠簡單和智能。
正所謂驅(qū)動科技發(fā)展的原因就是懶,為了使我們的小工具能夠傻瓜到只需要指定一個需要的目標(biāo)類型和要讀取的xml的地址就能實現(xiàn)目標(biāo)類實例的動態(tài)生成,下面的文字就誕生了。
需要解決的問題:
問,從xml文件到需要的目標(biāo)類實例需要幾步?
答,讀取XML文件,實例化一個目標(biāo)實例,賦值。
問題一:如何讀取XML文件
所以第一個問題就是如何讀取XML文件,參考這篇博客《c#讀取XML》,我們可知備選答案無非如下幾種:
1.XmlDocument的使用:
//XmlDocument使用 XmlDocument doc = new XmlDocument(); doc.Load("./Assets/xml-to-egg/xml-to-egg-test/Test.xml");XmlNode root = doc.SelectSingleNode("Test");
...
但是要注意的是,XmlDocument是讀取整個XML的,所以如果XML內(nèi)容過多,則會消費很多內(nèi)存。所以XML內(nèi)容過大時,不推薦使用XmlDocument。
2.XmlTextReader的使用:
//XmlTestReader的使用方法 XmlTextReader reader = new XmlTextReader("./Assets/xml-to-egg/xml-to-egg-test/Test.xml"); //使用read()方法向下讀取 while (reader.Read()) {..... }要說明與XmlDocument的最大區(qū)別,其實也很簡單,XmlReader使用Steam(流)來讀取文件,所以不會對內(nèi)存造成太大的消耗。XmlReader通過read()方法不斷向下讀取,我們就可以在這個過程中進行我們需要的操作。不過這個也不是我們的答案,我們選擇的答案在下面。
3.Linq to Xml
在System.Xml.Linq命名空間中,操作十分簡單和方便。
//Linq to Xml的使用 XElement xml = XElement.Load("./Assets/xml-to-egg/xml-to-egg-test/Test.xml"); //讀取的xml文件的元素都在生成的XElement的實例xml.Elements中。 string name = xml.Element("name").Value; ......可見十分簡單明了。傳入xml文件的路徑就會返回一個XElement類型的實例,并且xml文件的元素也都存入了XElement實例中。那么我們讀取XML文件的任務(wù)就交給它了。
讀取XML相關(guān)邏輯的代碼如下:
/// <summary> /// Sets the xml path. /// </summary> public static void SetXmlPath(string p) {path = p; } /// <summary> /// Loads the XML Files. /// </summary> private static XElement LoadXML() {if(path == null)return null;XElement xml = XElement.Load(path);return xml; }?
問題二:如何實例化一個目標(biāo)實例。
假設(shè)我們并不知道我們的這個動態(tài)讀取XML創(chuàng)建實例并賦值的小工具要處理的是什么類型的對象,那問題就來了,總不能每一個不同的類都對應(yīng)一套處理方法吧?那也太不智能且代碼太難以復(fù)用了。所以這里我們實例化一個目標(biāo)實例碰到的第一個問題就來了,也就是如何破解目標(biāo)類型的問題?
答案是使用泛型。
在實例化具體對象的時候,才確定類型,這樣就可以避免由于類型不同而導(dǎo)致的代碼無法復(fù)用的問題。
那么,下面我們的小工具---XMLToEgg就要出場了,對,就是一個處理引用類型的泛型類。
public static class XmlToEgg<T> where T : class {}可是光解決了實例類型的問題還是差一步啊,差點什么呢?對啊,那就是如何實例化一個泛型目標(biāo)實例。這也就是我們在實例化一個目標(biāo)實例時遇到的第二個問題。
答案是使用反射。
那下面繼續(xù)上代碼:
/// <summary>/// Creates the class initiate./// </summary>private static void CreateInitiate(){Type t = typeof(T);ConstructorInfo ct = t.GetConstructor(System.Type.EmptyTypes);target = (T)ct.Invoke(null);}當(dāng)然這里小匹夫假設(shè)我們的目標(biāo)類的構(gòu)造函數(shù)是不需要參數(shù)的,如果需要參數(shù)也很簡單,看官們自己可以查到這里就不贅述了。
好了,到這里我們?nèi)绾蝿?chuàng)建一個一開始我們不知道是什么類型,只有到創(chuàng)建的時候才知道是什么東西的類的實例的問題就解決了。(好繞)
問題三:如何為創(chuàng)建好的實例中的字段賦值
終于來到了我們的終極問題,也是我們最終的目標(biāo),實現(xiàn)從XML到目標(biāo)類實例的最后一步。在問題二的時候已經(jīng)說了,作為一個可以復(fù)用的工具,對處理的目標(biāo)類型應(yīng)該有包容性,那么既然連目標(biāo)類型都不確定,那么目標(biāo)類型的字段咋能確定呢?所以這個問題的本質(zhì)其實就是我不知道目標(biāo)類有啥字段啊。。。(如果你把字段寫死,是不是就沒有一點擴展性了。。。low爆有木有),那問題連環(huán)一個接一個,我既然不知道目標(biāo)類有啥字段,那我更不可能知道目標(biāo)類的字段的類型了吧。好,就算我啥都知道,我應(yīng)該怎么設(shè)呢?直接用instance.field = XXX? 圖樣圖森破。
所以問題的本質(zhì)是明確的:
正所謂“車到山前必有路,答案還是用反射”。只要能解決上面三個小問題,那么最后這一步就算是邁過去了。話不多說,下面上代碼:
/// <summary> /// attribute assignment, /// 由于反射中設(shè)置字段值的方法會涉及到賦值的目標(biāo)類型和當(dāng)前類型的轉(zhuǎn)化, /// 所以需要使用Convert.ChangeType進行類型轉(zhuǎn)化 /// </summary> public 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; }所以看代碼就很明白了,簡單介紹一下:
這樣,一個處理動態(tài)讀取XML創(chuàng)建類實例并賦值的類或者說小工具XMLToEgg就完成了,下面是完整的代碼。
/// <summary> /// XmlToEgg /// Created by chenjd /// http://www.cnblogs.com/murongxiaopifu/ /// https://github.com/chenjd/ /// </summary> 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 {public static class XmlToEgg<T> where T : class{private static string path;private static T target;static XmlToEgg(){}/// <summary>/// Sets the xml path./// </summary>public static void SetXmlPath(string p){path = p;}/// <summary>/// Loads the XML Files./// </summary>private static XElement LoadXML(){if(path == null)return null;XElement xml = XElement.Load(path);return xml;}/// <summary>/// Creates the class initiate./// </summary>private static void CreateInitiate(){Type t = typeof(T);ConstructorInfo ct = t.GetConstructor(System.Type.EmptyTypes);target = (T)ct.Invoke(null);}/// <summary>/// attribute assignment,/// 由于反射中設(shè)置字段值的方法會涉及到賦值的目標(biāo)類型和當(dāng)前類型的轉(zhuǎn)化,/// 所以需要使用Convert.ChangeType進行類型轉(zhuǎn)化/// </summary>public 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;}} }測試:
?
完整的項目代碼以及使用方法、測試可以從這里獲取:XMLToEgg(https://github.com/chenjd/Unity3D_XMLToEgg)
?
裝模作樣的聲明一下:本博文章若非特殊注明皆為原創(chuàng),若需轉(zhuǎn)載請保留原文鏈接(http://www.cnblogs.com/murongxiaopifu/p/4175395.html)及作者信息慕容小匹夫
更新(之前在游戲蠻牛更新了,忘了在這里同步)
有童鞋提出了為什么不介紹使用序列化和反序列化?小匹夫覺得這個問題挺好噠。那么就在這里回答一下:
1序列化&反序列化的應(yīng)用情景一般是類-->xml-->類有一個保存的概念在里面。這里主要介紹的是純粹從xml到類。如果覺得還是沒區(qū)別那么看下面。
2.聊聊XmlSerializer的實現(xiàn)。
? ?1)XmlSerializer首先你要告訴它你要序列化的類型。例如。XmlSerializer xs = new XmlSerializer(typeof(chenjiadong));
? ?2)XmlSerializer的構(gòu)造函數(shù)會使用 反射 去掃描這個類的內(nèi)容(用反射并不生成新的代碼)。
? ?3)之后會生成C#的方法去序列化這個類型(此時會生成新的代碼)。
? ?4)并且會動態(tài)編譯C#到IL??(這樣做當(dāng)然有好處,就是在序列化和反序列化進行的過程中無需反射,而是直接生成新的代碼去處理,速度上比反射好的多。但是在IOS上新的IL意味著什么呢?)
? ?5)所以,不管你是序列化,還是反序列化,都會有上面的4個步驟。
3.聊聊這篇文章的目的:細(xì)說的含義其實就是講下原理。你可以把文中的XmlToEgg就當(dāng)成一個類似處理工具,不過本文的目的是介紹XML的讀取,泛型和反射,XmlToEgg是個衍生品。而且其實它的使用也很簡單。
總結(jié)
以上是生活随笔為你收集整理的自己动手之使用反射和泛型,动态读取XML创建类实例并赋值的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用GoldenGate进行平台迁移和数
- 下一篇: ASP.NET MVC+EF框架+Eas