C#反射总结
C#反射總結
Reflection,中文翻譯為反射。這是.Net中獲取運行時類型信息的方式,.Net的應用程序由幾個部分:‘程序集(Assembly)’、‘模塊(Module)’、‘類型(class)’組成,而反射提供一種編程的方式,讓程序員可以在程序運行期獲得這幾個組成部分的相關信息,例如:Assembly類可以獲得正在運行的裝配件信息,也可以動態的加載裝配件,以及在裝配件中查找類型信息,并創建該類型的實例。Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素的信息,并且調用之。MethodInfo包含方法的信息,通過這個類可以得到方法的名稱、參數、返回值等,并且可以調用之。諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。
一、Type類于獲取類型信息
System.Type 類對于反射起著核心的作用。當反射請求加載的類型時,公共語言運行庫將為它創建一個 Type。您可以使用 Type 對象的方法、字段、屬性和嵌套類來查找有關該類型的所有信息。
大家運行一下下面的代碼根據結果分析一下就能比較清楚的理解Type了
?
獲取類型信息namespace?ConsoleApplication2
{
????class?Program
????{
????????static?void?Main(string[]?args)
????????{
????????????MyClass?m?=?new?MyClass();
????????????Type?type?=?m.GetType();
????????????Console.WriteLine("類型名:"?+?type.Name);
????????????Console.WriteLine("類全名:"+type.FullName);
????????????Console.WriteLine("命名空間名:"+type.Namespace);
????????????Console.WriteLine("程序集名:"+type.Assembly);
????????????Console.WriteLine("模塊名:"+type.Module);
????????????Console.WriteLine("基類名:"+type.BaseType);
????????????Console.WriteLine("是否類:"+type.IsClass);
????????????Console.WriteLine("類的公共成員:");
????????????MemberInfo[]?memberInfos?=?type.GetMembers();//得到所有公共成員
????????????foreach?(var?item?in?memberInfos)
????????????{
????????????????Console.WriteLine("{0}:{1}",item.MemberType,item);
????????????}
????????}
????????
????}
????class?MyClass
????{
????????public?string?m;
????????public?void?test()
????????{?}
????????public?int?MyProperty?{?get;?set;?}
????}
}
?
二、獲取程序集元數據
Assembly類定義了一個程序集,它是一個可重用、無版本沖突并且可自我描述的公共語言運行庫應用程序構造塊。因為程序集中是使用元數據進行自我描述的,所以我們就能通過其元數據得到程序集內部的構成。結合Assembly和反射能夠獲取程序集的元數據,但是首先要將程序集裝入內存中。可以使用Assembly類的多種靜態Load方法加載程序集。
下面的程序顯示程序集的信息
?
Codepublic?static?void?Main()
????????{
????????????//獲取當前執行代碼的程序集
????????????Assembly?assem?=?Assembly.GetExecutingAssembly();
????????????Console.WriteLine("程序集全名:"+assem.FullName);
????????????Console.WriteLine("程序集的版本:"+assem.GetName().Version);
????????????Console.WriteLine("程序集初始位置:"+assem.CodeBase);
????????????Console.WriteLine("程序集位置:"+assem.Location);
????????????Console.WriteLine("程序集入口:"+assem.EntryPoint);
????????????Type[]?types?=?assem.GetTypes();
????????????Console.WriteLine("程序集下包含的類型:");
????????????foreach?(var?item?in?types)
????????????{
????????????????Console.WriteLine("類:"+item.Name);
????????????}
????????????
????????}
?
三、動態加載類型
早綁定是在編譯時綁定對象類型,而晚綁定是在運行時才綁定對象的類型。利用反射可以實現晚綁定,即動態加載類型,并調用他們的方法,下邊是MSDN中的一個例子,詳細的解釋信息見注釋
?
動態加載類型namespace?ConsoleApplication2
{
????public?class?Example
????{
????????private?int?factor;
????????public?Example(int?f)
????????{
????????????factor?=?f;
????????}
????????public?int?SampleMethod(int?x)
????????{
????????????Console.WriteLine("\nExample.SampleMethod({0})?executes.",?x);
????????????return?x?*?factor;
????????}
????????public?static?void?Main()
????????{
????????????//獲取當前執行代碼的程序集
????????????Assembly?assem?=?Assembly.GetExecutingAssembly();
????????????Console.WriteLine("Assembly?Full?Name:");
????????????Console.WriteLine(assem.FullName);
????????????//?The?AssemblyName?type?can?be?used?to?parse?the?full?name.
????????????AssemblyName?assemName?=?assem.GetName();
????????????Console.WriteLine("\nName:?{0}",?assemName.Name);
????????????Console.WriteLine("Version:?{0}.{1}",
????????????????assemName.Version.Major,?assemName.Version.Minor);
????????????Console.WriteLine("\nAssembly?CodeBase:");
????????????Console.WriteLine(assem.CodeBase);
????????????//?從程序集眾創建一個Example實例并且用object類型的引用o指向它,同時調用一個輸入參數的構造函數
????????????Object?o?=?assem.CreateInstance("ConsoleApplication2.Example",?false,
????????????????BindingFlags.ExactBinding,
????????????????null,?new?Object[]?{?2?},?null,?null);
????????????//構造Example類的一個晚綁定的方法SampleMethod??
????????????MethodInfo?m?=?assem.GetType("ConsoleApplication2.Example").GetMethod("SampleMethod");
????????????//調用剛才實例化好的Example對象o中的SampleMethod方法,傳入的參數為42
????????????Object?ret?=?m.Invoke(o,?new?Object[]?{?42?});
????????????Console.WriteLine("SampleMethod?returned?{0}.",?ret);
????????????Console.WriteLine("\nAssembly?entry?point:");
????????????Console.WriteLine(assem.EntryPoint);
????????}
????}
?
反射特性:
[Table(Name="dbo.[User]")]
public partial class User
{
當C#編譯器發現這個屬性有一個特性Table時,首先會把字符串Attribute添加到這個名稱的后面,形成一個組合名稱TableAttribute,然后在其搜索路徑的所有命名空間中搜索有相同類名的類。但要注意,如果該特性名結尾是Attribute,編譯器就不會把該字符串加到組合名稱中。所有的特性都是從System.Attribute類型上面派生的。
接著我們來看一下Table特性的定制格式
[AttributeUsageAttribute(AttributeTargets.Class, Inherited=true,AllowMultiple=false)]
public class TalbeAttribute:Attribute
{
??? 在定義類型時使用System.AttributeUsage特性來表明這個自定義特性的使用范圍,這里使用了Class樣式,表示TableAttribute特性只能用在其它的Class類型前面,若放置在Interface或Struct類型前面,或者放在對象成員的前面則會出現編譯錯誤。這里還是用語句 AllowMultiple=false 語句來表明對于一個類型,該特性只能用一次,若一個Class類型前面出現多個TableAttribute,則會出現編譯錯誤。若設置AllowMultiple=true,則該特性可以多次定義,也就是一個Class類型前面可以出現多個相同類型的特性。不過這里我們假設一個對象只能映射到一個數據表上,沒有多重映射,因此就指明對同一個類型該特性不能多次使用。Inherited參數設定為true,就表示應用到類或接口上的特性也可以自動應用到所派生的類或接口上。
我們再看一下定制TalbeAttribute特性的完整例子:
?
?[AttributeUsageAttribute(AttributeTargets.Class,?Inherited?=?false,?AllowMultiple?=?false)]????public?class?TableAttribute?:?Attribute
????{
????????//保存表名的字段
????????private?string?_tableName;
????????public?TableAttribute()
????????{
????????}
????????public?TableAttribute(string?tableName)
????????{
????????????this._tableName?=?tableName;
????????}
????????///?<summary>
????????///?映射的表名(表的全名:模式名.表名)
????????///?</summary>
????????public?string?TableName
????????{
????????????set
????????????{
????????????????this._tableName?=?value;
????????????}
????????????get
????????????{
????????????????return?this._tableName;
????????????}
????????}
????}
?
?特性也是一個Class類型,可以有多個構造函數,就像C#的new語句一樣,我們向類型附加特性時可以使用不同的初始化參數來指明使用特性的那個構造函數。我們附加特性時還可以使用“屬性名=屬性值”的方法來直接指明特性的屬性值。該特性中定義了一個TableName屬性,該屬性就是被修飾的對象所映射的數據庫表的名稱。
下面我們舉一個使用特性來進行O/RMapping的例子,也就是將對象轉化成Sql語句
用戶類:
User類????[Table("User")]
????public?class?User
????{
????????[Colum("userID",?DbType?=?DbType.Int32)]
????????public?int?UserID?{?get;?set;?}
????????[Colum("UserName",?DbType?=?DbType.String)]
????????public?string?UserName?{?get;?set;?}
????}
?
表特性
?
表特性[AttributeUsageAttribute(AttributeTargets.Class,?Inherited?=?false,?AllowMultiple?=?false)]
????public?class?TableAttribute?:?Attribute
????{
????????//保存表名的字段
????????private?string?_tableName;
????????public?TableAttribute()
????????{
????????}
????????public?TableAttribute(string?tableName)
????????{
????????????this._tableName?=?tableName;
????????}
????????///?<summary>
????????///?映射的表名(表的全名:模式名.表名)
????????///?</summary>
????????public?string?TableName
????????{
????????????set
????????????{
????????????????this._tableName?=?value;
????????????}
????????????get
????????????{
????????????????return?this._tableName;
????????????}
????????}
????}
?
列特性:
?
列特性?[AttributeUsageAttribute(AttributeTargets.Property,?Inherited?=?false,?AllowMultiple?=?false)]
????public?class?ColumAttribute?:?Attribute
????{
????????private?string?_columName;
????????private?DbType?_dbType?;
????????public?ColumAttribute()
????????{
????????}
????????public?ColumAttribute(string?columName)
????????????:?this()
????????{
????????????this._columName?=?columName;
????????}
?????
????????public?ColumAttribute(string?columName,?DbType?dbType)
????????????:?this(columName)
????????{
????????????this._dbType?=?dbType;
????????}
????????//列名
????????public?virtual?string?ColumName
????????{
????????????set
????????????{
????????????????this._columName?=?value;
????????????}
????????????get
????????????{
????????????????return?this._columName;
????????????}
????????}
????????//描述一些特殊的數據庫類型
????????public?DbType?DbType
????????{
????????????get?{?return?_dbType;?}
????????????set?{?_dbType?=?value;?}
????????}
????}
?
?
ORMHelp?public?class?ORMHelp
????{
????????public?void?Insert(object?table)
????????{
????????????Type?type?=?table.GetType();
????????????//定義一個字典來存放表中字段和值的對應序列
????????????Dictionary<string,?string>?columValue?=?new?Dictionary<string,?string>();
????????????StringBuilder?SqlStr=new?StringBuilder();
????????????SqlStr.Append("insert?into?");
????????????//得到表名子
????????????TableAttribute?temp?=?(TalbeAttribute)type.GetCustomAttributes(typeof(TalbeAttribute), false).First();
????????????SqlStr.Append(temp.TableName);
????????????SqlStr.Append("(");
????????????PropertyInfo[]?Propertys=type.GetProperties();
????????????foreach?(var?item?in?Propertys)
????????????{
????????????????object[]?attributes?=?item.GetCustomAttributes(false);
????????????????foreach?(var?item1?in?attributes)
????????????????{
????????????????????//獲得相應屬性的值
???????????????????string?value=?table.GetType().InvokeMember(item.Name,?System.Reflection.BindingFlags.GetProperty,?null,?table,?null).ToString();
????????????????????ColumAttribute?colum?=?item1?as?ColumAttribute;
????????????????????if?(colum?!=?null)
????????????????????{
????????????????????????columValue.Add(colum.ColumName,value);
????????????????????}
????????????????}
????????????}
????????????//拼插入操作字符串
????????????foreach?(var?item?in?columValue)
????????????{
????????????????SqlStr.Append(item.Key);
????????????????SqlStr.Append(",");
????????????}
????????????SqlStr.Remove(SqlStr.Length-1,?1);
????????????SqlStr.Append(")?values('");
????????????foreach?(var?item?in?columValue)
????????????{
????????????????SqlStr.Append(item.Value);
????????????????SqlStr.Append("','");
????????????}
????????????SqlStr.Remove(SqlStr.Length?-?2,?2);
????????????SqlStr.Append(")");
????????????Console.WriteLine(SqlStr.ToString());
????????}
????}
SqlStr中的內容為insert into User(userID,UserName) values('1','lfm')
前端使用代碼:
?
前端代碼static?void?Main(string[]?args)
????????{
????????????ORMHelp?o?=?new?ORMHelp();
????????????User?u?=?new?User()?{?UserID=1,UserName="lfm"};
????????????o.Insert(u);
????????}
?
應用
?
例子這個東西其實挺難弄得,弄個簡單的,雖然能說明問題但卻容易讓人覺得沒實用價值,弄個有實用價值卻又往往牽扯很多別的技術甚至牽扯很多業務邏輯,看起來很復雜很難懂。在這里我盡量追求幾個有實用價值又不復雜的例子。
1、使用反射通過讀取配置文件來動態的創建相關類的對象
我們先來看看Main函數和需要動態加載的對象在同一個程序集的情況
結構圖:
接口
?
?
接口interface?ILog
????{
????????bool?Write(string?message);
????????bool?Write(Exception?ex);
????}
?
?
?
?
TextFileLogclass?TextFileLog?:?ILog
????{
????????public?bool?Write(string?message)
????????{
????????????string?fileDir?=?ConfigurationManager.AppSettings["LogTarget"].ToString();
????????????using?(StreamWriter?w?=?File.AppendText(fileDir))
????????????{
????????????????//?w.Write("?Log?Entry?:?");
????????????????w.WriteLine("發生時間{0}",?DateTime.Now.ToLocalTime().ToString());
????????????????w.WriteLine("日志內容為:{0}",?message);
????????????????w.WriteLine("-------------------------------");
????????????????//?Update?the?underlying?file.
????????????????w.Flush();
????????????????w.Close();
????????????}
????????????return?true;
????????}
????????public?bool?Write(Exception?ex)
????????{
????????????Write(ex.Message);
????????????return?true;
????????}
????}
?
?
?
?
XmlFileLogclass?XmlFileLog?:?ILog
????{
????????public?bool?Write(string?message)
????????{
????????????string?xmlFilePath?=?ConfigurationManager.AppSettings["LogTarget"].ToString();
????????????if?(File.Exists(xmlFilePath))
????????????{
????????????????XmlDocument?doc?=?new?XmlDocument();
????????????????doc.Load(xmlFilePath);
????????????????XmlDocumentFragment?docFrag?=?doc.CreateDocumentFragment();
????????????????XmlNode?nod?=?doc.SelectSingleNode("Logs");
????????????????docFrag.InnerXml?=?"<Log><Time>"?+?DateTime.Now.ToLocalTime().ToString()
????????????????????+?"</Time><Message>"?+?message?+?"</Message></Log>";
????????????????nod.AppendChild(docFrag);
????????????????doc.Save(xmlFilePath);
????????????????return?true;
????????????}
????????????else
????????????{
????????????????XmlWriterSettings?settings?=?new?XmlWriterSettings();
????????????????settings.Indent?=?true;?????//設置縮進???????
????????????????settings.ConformanceLevel?=?ConformanceLevel.Auto;
????????????????settings.IndentChars?=?"?";
????????????????settings.OmitXmlDeclaration?=?false;
????????????????using?(XmlWriter?writer?=?XmlWriter.Create(xmlFilePath,?settings))
????????????????{
????????????????????//Start?writing?the?XML?document
????????????????????writer.WriteStartDocument(false);
????????????????????//Start?with?the?root?element
????????????????????writer.WriteStartElement("Logs");
????????????????????writer.WriteStartElement("Log");
????????????????????writer.WriteStartElement("Time");
????????????????????writer.WriteString(DateTime.Now.ToLocalTime().ToString());
????????????????????writer.WriteEndElement();
????????????????????writer.WriteStartElement("Message");
????????????????????writer.WriteString(message);
????????????????????writer.WriteEndElement();
????????????????????writer.WriteEndElement();
????????????????????writer.WriteEndDocument();
????????????????????//Flush?the?object?and?write?the?XML?data?to?the?file
????????????????????writer.Flush();
????????????????????return?true;
????????????????}
????????????}
????????}
????????public?bool?Write(Exception?ex)
????????{
????????????Write(ex.Message);
????????????return?true;
????????}
????}
?
?
?
?
App.config配置<?xml?version="1.0"?encoding="utf-8"??>
<configuration>
??<appSettings>
????<add?key="LogType"?value="LogClassLibrary.TextFileLog"/>
????<!--
????本程序集配置
????<add?key="LogType"?value="ConsoleApplication2.Log例子.TextFileLog"/>
????-->
????<!--?XmlFileLog??TextFileLog-->
????<add?key="LogTarget"?value="c:\log.txt"/>
??</appSettings>
</configuration>
?
?
?
?
主程序?public?static?void?Main()
????????{
????????????#region?同程序集下
????????????System.Type?type?=?System.Type.GetType(ConfigurationManager.AppSettings["LogType"].ToString());
????????????ILog?log?=?(ILog)Activator.CreateInstance(type);
????????????log.Write(new?Exception("異常測試"));
????????????#endregion
????????}
?
?
如果在不同的程序集下,那主函數和配置會略有不同
?
?
不同程序集主函數?public?static?void?Main()
????????{
????????????#region?不同程序集
????????????string?assemblyPath?=?Path.Combine(Environment.CurrentDirectory,?"LogClassLibrary.dll");
????????????Assembly?a?=?Assembly.LoadFrom(assemblyPath);
????????????Type?type?=?a.GetType(ConfigurationManager.AppSettings["LogType"].ToString());
????????????LogClassLibrary.ILog?log?=?(LogClassLibrary.ILog)type.InvokeMember(null,BindingFlags.CreateInstance,null,null,null);
????????????log.Write(new?Exception("異常測試"));
????????????#endregion
????????}
?
?
這部分源碼下載
源碼下載
2、插件編程技術
插件是指遵循一定的接口規范、可以動態加載和運行的程序模塊。從上面的例子可以看出,通過反射可以非常方便的動態加載程序集。因此,利用反射的動態加載代碼能力,可以很容易的實現插件。插件編程的要點是使用接口來定義插件的功能特征。插件的宿主程序通過接口來確認、裝載和執行插件的功能,實現插件功能的所有類都必須實現定義插件的接口。
這里只是選貼一部分代碼,詳細分析請看源碼
結構圖
?
接口部分
?
?
接口public?interface?IHost
????{
????????List<ILog>?Plugins?{?get;?}
????????int?LoadPlugins(string?path);
????????ILog?GetLog(string?name);
????}
?public?interface?ILog
????{
????????bool?Write(string?message);
????????bool?Write(Exception?ex);
????}
?
?
宿主實現
?
?
宿主實現?public?class?Host?:?IHost
????{
????????private?List<ILog>?plugins?=?new?List<ILog>();
????????#region?IHost?成員
????????public?List<ILog>?Plugins
????????{
????????????get?{?return?plugins;?}
????????}
????????public?int?LoadPlugins(string?path)
????????{
????????????string[]?assemblyFiles?=?Directory.GetFiles(path,?"*.dll");
????????????foreach?(var?file?in?assemblyFiles)
????????????{
????????????????Assembly?assembly?=?Assembly.LoadFrom(file);
????????????????foreach?(var?type?in?assembly.GetExportedTypes())
????????????????{
????????????????????if?(type.IsClass?&&?typeof(ILog).IsAssignableFrom(type))
????????????????????{
????????????????????????ILog?plugin?=?Activator.CreateInstance(type)?as?ILog;
????????????????????????plugins.Add(plugin);
????????????????????}
????????????????}
????????????}
????????????return?plugins.Count;
????????}
????????public?ILog?GetLog(string?name)
????????{
????????????foreach?(var?item?in?plugins)
????????????{
????????????????if?(item.GetType().ToString()==name)
????????????????{
????????????????????return?item;
????????????????}
????????????????
????????????}
????????????return?null;
????????}
????????#endregion
????}
?
?
ILog的實現和上例基本一樣,請參考
主程序代碼
?
?
主程序代碼static?void?Main(string[]?args)
????????{
????????????Host.Host?host?=?new?Host.Host();
????????????host.LoadPlugins(".");
????????????InterfaceLayer.ILog?log?=?host.GetLog(ConfigurationManager.AppSettings["LogType"].ToString());
????????????log.Write(new?Exception("異常測試"));
????????}
?
?
插件編程源碼下載源碼下載
3、分析對象,得到對象中的屬性值
大家使用應都用過asp.net中的DropdownList,在綁定其值的時候絕大多數情況下我們做的都是同樣的事情,獲得數據源,根據數據源中的某些列綁定控件,下邊我們來說說通用情況的處理方式。我們只需要提供數據集合,以及需要綁定到控件的兩個屬性(text,value)名即可。
?
?
Code轉載于:https://www.cnblogs.com/strivers/archive/2010/11/27/1889744.html
總結
- 上一篇: 在多核CPU上安装SQL SERVER
- 下一篇: 推荐《淘宝的可伸缩高性能互联网架构 》