例子這個東西其實挺難弄得,弄個簡單的,雖然能說明問題但卻容易讓人覺得沒實用價值,弄個有實用價值卻又往往牽扯很多別的技術甚至牽扯很多業務邏輯,看起來很復雜很難懂。在這裡我盡量追求幾個有實用價值又不復雜的例子。
1、使用反射通過讀取配置文件來動態的創建相關類的對象
我們先來看看Main函數和需要動態加載的對象在同一個程序集的情況
結構圖:
接口
interface ILog
{
bool Write(string message);
bool Write(Exception ex);
}
TextFileLog
class 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;
}
}
XmlFileLog
class 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、插件編程技術
插件是指遵循一定的接口規范、可以動態加載和運行的程序模塊。從上面的例子可以看出,通過反射可以非常方便的動態加載程序集。因此,利用反射的動態加載代碼能力,可以很容易的實現插件。插件編程的要點是使用接口來定義插件的功能特征。插件的宿主程序通過接口來確認、裝載和執行插件的功能,實現插件功能的所有類都必須實現定義插件的接口。
這裡只是選貼一部分代碼,詳細分析請看源碼
結構圖
接口部分
publicinterfaceIHost
{
List<ILog>Plugins{get;}
intLoadPlugins(stringpath);
ILogGetLog(stringname);
}
publicinterfaceILog
{
boolWrite(stringmessage);
boolWrite(Exceptionex);
}
宿主實現
publicclassHost:IHost
{
privateList<ILog>plugins=newList<ILog>();
#regionIHost成員
publicList<ILog>Plugins
{
get{returnplugins;}
}
publicintLoadPlugins(stringpath)
{
string[]assemblyFiles=Directory.GetFiles(path,"*.dll");
foreach(varfileinassemblyFiles)
{
Assemblyassembly=Assembly.LoadFrom(file);
foreach(vartypeinassembly.GetExportedTypes())
{
if(type.IsClass&&typeof(ILog).IsAssignableFrom(type))
{
ILogplugin=Activator.CreateInstance(type)asILog;
plugins.Add(plugin);
}
}
}
returnplugins.Count;
}
publicILogGetLog(stringname)
{
foreach(variteminplugins)
{
if(item.GetType().ToString()==name)
{
returnitem;
}
}
returnnull;
}
#endregion
}
ILog的實現和上例基本一樣,請參考
主程序代碼
staticvoidMain(string[]args)
{
Host.Hosthost=newHost.Host();
host.LoadPlugins(".");
InterfaceLayer.ILoglog=host.GetLog(ConfigurationManager.AppSettings["LogType"].ToString());
log.Write(newException("異常測試"));
}
3、分析對象,得到對象中的屬性值
大家使用應都用過asp.net中的DropdownList,在綁定其值的時候絕大多數情況下我們做的都是同樣的事情,獲得數據源,根據數據源中的某些列綁定控件,下邊我們來說說通用情況的處理方式。我們只需要提供數據集合,以及需要綁定到控件的兩個屬性(text,value)名即可。
publicclassDDlControl
{
privateListControlunderlyingList;
publicDDlControl(ListControlunderlyingList)
{
this.underlyingList=underlyingList;
}
publicvoidAdd(IDDLddl)
{
underlyingList.Items.Add(newListItem(ddl.Name,ddl.Value));
}
publicvoidAdd<T>(Tt,stringnameStr,stringvalueStr)
{
stringname=Convert.ToString(t.GetType().InvokeMember
(nameStr,System.Reflection.BindingFlags.GetProperty,null,t,null));
stringvalue=Convert.ToString(t.GetType().InvokeMember
(valueStr,System.Reflection.BindingFlags.GetProperty,null,t,null));
Add(newDDLStruct(name,value));
}
publicvoidClear()
{
underlyingList.Items.Clear();
}
publicIDDLSelectedItem
{
get
{
ListItemitem=underlyingList.SelectedItem;
returnnewDDLStruct(item.Text,item.Value);
}
}
publicvoidBindTo<T>(IEnumerable<T>list,stringnameStr,stringvalueStr)
{
Clear();
foreach(variteminlist)
{
Add<T>(item,nameStr,valueStr);
}
}
publicstringSelectValue
{
get
{
returnunderlyingList.SelectedValue;
}
set
{
underlyingList.SelectedValue=value;
}
}
}
publicstructDDLStruct
{
publicDDLStruct(stringname,stringvalue)
{
this.name=name;
this.value=value;
}
privatestringname;
privatestringvalue;
publicstringName
{
get{returnname;}
}
publicstringValue
{
get{returnvalue;}
}
}