動態代碼的使用(反射和動態產生類)
在軟體開發尤其是架構和底層開發時,為了更靈活的控制碼,常常需要進行一些動態操作。比如根
據使用者的輸入等動態調用類中的方法或者根據資料庫表結 構、使用者要求動態產生一些類,然後再動態
的調用類中的方法。當然使用這些方式時會對效能有一點影響,具體使用過程中可以根據實際情況來定,不
過一般的 B/S開發中主要的瓶頸還是在資料庫操作和網速方面,這點影響應該可以忽略的
一、反射的使用
可以使用反射動態地建立類型的執行個體,將類型綁定到現有對象,或從現有對象中擷取類型。然後,可以
調用類型的方法或訪問其欄位和屬性。
需要使用的命名空間:System.Reflection
反射的作用很多,下面的例子主要是看一下怎麼動態調用類中的方法。
例子類
class ReflTest1
{
private string _prop1;
public string Prop1
{
get { return _prop1; }
set { _prop1 = value; }
}
public void Write1(string strText)
{
Console.WriteLine("111111111:" + strText);
}
public void Write2(string strText)
{
Console.WriteLine("222222222:" + strText);
}
public void MyWrite(string strText)
{
Console.WriteLine("3333333333:" + strText);
}
}
這個例子中提供了三個方法和一個屬性,下面的代碼來動態調用它們:
string strText = "abcd";
BindingFlags flags = (BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly);
Type t = typeof(ReflTest1);
MethodInfo[] mi = t.GetMethods(flags);
Object obj = Activator.CreateInstance(t);
foreach (MethodInfo m in mi)
{
if (m.Name.StartsWith("Write"))
{
m.Invoke(obj, new object[] { strText });
} [Page]
}
MethodInfo mMy = t.GetMethod("MyWrite");
if (mMy != null)
{
mMy.Invoke(obj, new object[] { strText });
}
BindingFlags用來設定要取得哪些類型的方法,然後我們就可以取得這些方法來動態調用。(當然為
了可以迴圈的調用方法,在方法的命名方面可以自己指定一個規則)
二、動態產生類
我們可以在程式運行過程中調用.NET中提供的編譯類,動態將一段string編譯成一個類,然後再通過
反射來調用它
需要使用的命名空間:System.CodeDom System.CodeDom.Compiler Microsoft.CSharp
System.Reflection
動態建立、編譯類的代碼如下:
public static Assembly NewAssembly()
{
//建立編譯器執行個體。
provider = new CSharpCodeProvider();
//設定編譯參數。
paras = new CompilerParameters();
paras.GenerateExecutable = false;
paras.GenerateInMemory = true;
//建立動態代碼。
StringBuilder classSource = new StringBuilder();
classSource.Append("public class DynamicClass \n");
classSource.Append("{\n");
//建立屬性。
classSource.Append(propertyString("aaa"));
classSource.Append(propertyString("bbb"));
classSource.Append(propertyString("ccc"));
classSource.Append("}");
System.Diagnostics.Debug.WriteLine(classSource.ToString());
//編譯代碼。
CompilerResults result = provider.CompileAssemblyFromSource(paras,
classSource.ToString());
//擷取編譯後的程式集。
Assembly assembly = result.CompiledAssembly; [Page]
return assembly;
}
private static string propertyString(string propertyName)
{
StringBuilder sbProperty = new StringBuilder();
sbProperty.Append(" private int _" + propertyName + " = 0;\n");
sbProperty.Append(" public int " + "" + propertyName + "\n");
sbProperty.Append(" {\n");
sbProperty.Append(" get{ return _" + propertyName + ";} \n");
sbProperty.Append(" set{ _" + propertyName + " = value; }\n");
sbProperty.Append(" }");
return sbProperty.ToString();
}
propertyString方法就是用來拼字字串的
整個代碼比較簡單,主要步驟就是:1、拼字類的字串 2、調用CSharpCodeProvider類進行編譯得到
程式集(assembly)
接下來就可以利用之前反射的方法來動態調用這個類中的屬性了:
Assembly assembly = NewAssembly();
object Class1 = assembly.CreateInstance("DynamicClass");
ReflectionSetProperty(Class1, "aaa", 10);
ReflectionGetProperty(Class1, "aaa");
object Class2 = assembly.CreateInstance("DynamicClass");
ReflectionSetProperty(Class1, "bbb", 20);
ReflectionGetProperty(Class1, "bbb");
DynamicClass是我動態類的類名,aaa和bbb是其中的屬性
ReflectionSetProperty和ReflectionGetProperty代碼如下:
給屬性賦值
private static void ReflectionSetProperty(object objClass, string propertyName, int
value)
{
PropertyInfo[] infos = objClass.GetType().GetProperties();
foreach (PropertyInfo info in infos)
{
if (info.Name == propertyName && info.CanWrite) [Page]
{
info.SetValue(objClass, value, null);
}
}
}
取得屬性的值
private static void ReflectionGetProperty(object objClass, string propertyName)
{
PropertyInfo[] infos = objClass.GetType().GetProperties();
foreach (PropertyInfo info in infos)
{
if (info.Name == propertyName && info.CanRead)
{
System.Console.WriteLine(info.GetValue(objClass, null));
}
}
}
添加自訂的dll和命名空間,注意要絕對路徑(系統本身的不用,不過codedom的版本要對上,比如2005的codedom加linq的dll還是要絕對路徑),至於怎麼取絕對路徑,自己來吧
CodeCompileUnit m_CodeCompileUnit = new CodeCompileUnit();
CodeNamespace m_CodeNameSpace = new CodeNamespace("xml.tables");
m_CodeCompileUnit.ReferencedAssemblies.Add(@"E:\本地測試\動態產生代碼\動態產生代碼\Bin\RDll.dll");
m_CodeNameSpace.Imports.Add(new System.CodeDom.CodeNamespaceImport("RDll"));