实现简单的CSharpShell -- OrcShell (2) 类型浏览、执行代码片断与其它

 二、类型管理

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 == nullreturn;

            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 == nullreturn;

            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 == nullreturn;

            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 == nullreturn;

            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 });
            }

        }

其中 CodeProviderCompilerParameters   Context 中初始化:

            CodeProvider = new CSharpCodeProvider(new Dictionary<stringstring>() "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 保存为环境变量,变量名称为 nameMy(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、指令的自动完成。

留待后续。

 

posted @ 2008-02-29 10:25  xiaotie  阅读(1386)  评论(3编辑  收藏  举报