二、類型管理
1、程序集與類型的管理
在Context初始化時便將AppDomain中的類型全部加載並交給TypeManager管理:
public Context() { …… TypeManager = new TypeManager(); Assemblys = new Dictionary<String, Assembly>(); Assembly[] al = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in al) { AddAssembly(a); } AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler (CurrentDomain_AssemblyLoad); …… } private void AddAssembly(Assembly a) { if (a != null) { Assemblys.Add(a.FullName, a); Type[] tl = a.GetTypes(); foreach (Type t in tl) { if(!t.FullName.StartsWith("<PrivateImplementationDetails>")) TypeManager.AddType(t); } } } void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) { Assembly a = args.LoadedAssembly; if (!Assemblys.ContainsKey(a.FullName)) { AddAssembly(a); } }
開發時發現,程序集中有一批類型名字以"<PrivateImplementationDetails>"開頭的類型,貌 似時臨時類型,這些東西數量較多,干脆把它屏蔽掉了。
2、進出命名空間
在CdClassCmdHandler 中實現,目前不支持級聯操作,即:cdc ..;cdc .; cdc namespaceName這樣 是可以的,cdc ../ns1/ns2 這樣是不支持的。輸入的命名空間名稱可以只是部分,程序自動進行匹配, 如只有1個匹配項則自動進入該項,否則不進行操作,同時打印所有匹配項。
3、列出命名空間和類型
在 ListClassCmdHandler 中實現,支持正則表達式匹配。幕後工作由TypeDictionary在做:
Context.TypeManager.Now.ListDir(match); Context.TypeManager.Now.ListType(match); public void ListType(String match) { Regex re = null; if (match != null) { re = new Regex(match); } foreach (Type t in Types.Values) { String name = t.Name; if (re != null) { if (!re.IsMatch(name)) continue; } Console.WriteLine("C:\t" + Context.EnsureAtLeastLength(name,20) + "\t" + t.FullName); } } public void ListDir(String match) { Regex re = null; if (match != null) { re = new Regex(match); } foreach (TypeDictionary dic in SubTypeDictionary.Values) { String name = dic.Name; if (re != null) { if (!re.IsMatch(name)) continue; } Console.WriteLine("N:\t" + Context.EnsureAtLeastLength(name, 20) + "\t" + dic.FullName); } }
4、查看類型
擴展方法確實是好東西,有了它這裡實現起來很簡單。在 ClassExtensionMethods裡 實現:
public static class ClassExtensionMethods { …… public static void methods(this Type t) { foreach (MethodInfo mi in t.GetMethods()) { Console.WriteLine(" " + mi); } } public static void methods(this Object obj) { if (obj == null) return; methods(obj.GetType()); } public static void props(this Type t) { foreach (PropertyInfo pi in t.GetProperties()) { Console.WriteLine(" " + pi); } } public static void props(this Object obj) { if (obj == null) return; props(obj.GetType()); } public static void members(this Type t) { foreach (MemberInfo mi in t.GetMembers()) { Console.WriteLine(" " + mi); } } public static void members(this Object obj) { if (obj == null) return; members(obj.GetType()); } public static void creaters(this Type t) { foreach (ConstructorInfo ci in t.GetConstructors()) { Console.WriteLine(" " + ci); } } public static void creaters(this Object obj) { if (obj == null) return; creaters(obj.GetType()); } }
三、執行代碼片斷
在 CscCmdHandler 中實現。核心方法為 CscCmdHandler.Run(),代碼如下:
public override void Run() { if (String.IsNullOrEmpty(InputCmdString)) return; String fullCmd = String.Empty; if (Context.TypeManager.Now != Context.TypeManager.Root) { fullCmd += " using " + Context.TypeManager.Now.FullName + ";"; } fullCmd += @" using System; using System.IO; using System.Text; using System.Collections.Generic; using Orc.Shell.Core; namespace Orc.Shell.Core.Dynamic { public class DynamicClass { public Orc.Shell.Core.Context Context; public void Save(String name, Object obj) { Context.Save(name,obj); } public Object My(String name) { return Context[name]; } public void MethodInstance(Context context) { Context = context; " + InputCmdString + @"; } } }"; CompilerResults cr = Context.CodeProvider.CompileAssemblyFromSource (Context.CompilerParameters, fullCmd); if (Context.Debug) { Console.WriteLine("Source:"); Console.WriteLine("--------------------------------"); Console.WriteLine(fullCmd); Console.WriteLine("--------------------------------"); Console.WriteLine("Results"); } if (cr.Errors.HasErrors) { Console.WriteLine("編譯錯誤:"); foreach (CompilerError err in cr.Errors) { if (Context.Debug) { Console.WriteLine(String.Format("line {0}: {1}", err.Line, err.ErrorText)); } else { Console.WriteLine(err.ErrorText); } } } else { Assembly assem = cr.CompiledAssembly; Object dynamicObject = assem.CreateInstance ("Orc.Shell.Core.Dynamic.DynamicClass"); Type t = assem.GetType("Orc.Shell.Core.Dynamic.DynamicClass"); MethodInfo minfo = t.GetMethod("MethodInstance"); minfo.Invoke(dynamicObject, new Object[] { Context }); } }
其中 CodeProvider,CompilerParameters 在 Context 中初始化:
CodeProvider = new CSharpCodeProvider(new Dictionary<string, string> () { { "CompilerVersion", "v3.5" } }); CompilerParameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll", "Orc.Shell.Core.dll", "OrcShell.exe" }); CompilerParameters.GenerateExecutable = false; CompilerParameters.GenerateInMemory = true;
可以通過 Save(String name, Object obj) 和 My(String name) 來同環境進行交互。其中,Save (String name, Object obj) 是將代碼片斷中的對象 obj 保存為環境變量,變量名稱為 name。My (String name)取出名稱為name 的環境變量,加載到代碼段上。my 指令可以查看所有環境變量。
采用$name的方式操作環境變量更簡介、直觀,但這樣一來代碼難度加大不少,沒想到什麼簡潔的實現 ,就沒采用。
四、其它
1、擴展方法
對於常用的方法通過擴展方法來方便使用。如,打印一個對象 obj 到控制台上,正常寫法是 System.Console.WriteLine(obj.ToString()),比較麻煩,通過擴展方法,可以使它簡化為:obj.p();相 關代碼如下:
public static class ClassExtensionMethods { public static void Print(this Object obj) { Console.WriteLine(obj); } public static void p(this Object obj) { Print( obj ); } public static void P(this Object obj) { Print(obj); } public static void print(this Object obj) { Print(obj); } …… }
2、變量縮寫(Alias)
指令縮寫可明顯降低操作量。可通過編輯程序集目錄下的 Alias.xml 文件來添加、刪除或更改指令縮 寫。
Alias 指令可以查看目前的指令縮寫。
五、缺乏的功能。
到現在為止,OrcShell只實現了Shell的雛型。由於只開發了一個晚上,測試也不是很完善,另外許多 重要功能還未涉及,主要包括:
1、手動加載程序集;
2、常用系統管理功能,如常用的Shell 指令;
3、遠程控制;
4、指令的自動完成。
留待後續。