关于CAD命令系统的改进

本文介绍了如何结合使用特性、反射和字典来优化CAD命令系统,提高程序性能和扩展性。通过定义特性类和命令类,以及自定义特性,实现了将命令与方法高效匹配。此外,通过初始化字典并加载方法特性,避免了在输入命令时的重复遍历,从而显著提升性能。最后,通过创建初始化类对象并利用字典快速查找命令,实现了命令与方法的高效对应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

笔者的上一片文章中用了四种方式来实现CAD命令系统的功能,也提到了各自的优缺点,也提出了自己的观点,使用第四种方式能获得非常好的扩展性体验。然而,仔细观察一下代码,读者会发现,第四种实现方法是通过三层循环(第一层循环是为了连续读取用户输入的命令。),将用户输入的命令与方法的特性值一个一个做比对而实现的。也就是说,在用户输入完命令后,程序就开始遍历方法数组,然后获得某个方法的特性,然后再遍历这个方法的特性,并将特性值与用户输入的命令做比较。


如果方法的数量不多,且方法的特性性比较少时,这么做不会有太大的问题。但是对于一个CAD系统,被调用的方法有上千个,而每个方法又有多个不同的命令。那么要查找到需要的命令,可能就需要循环上万次。这绝对是件耗费时间、考验机器性能的事情。那么有什么方法能更好的解决这个问题呢?


在上一篇文章中笔者提到过字典类。字典类的运算速度非常快,因为他使用了散列算法,可以在微秒级别上迅速找出给定的键的值。既然它的速度这么快,有没有办法把反射和字典这两种方法结合起来呢?


答案是肯定的,关键点就在MehodInfo类!


在上一篇文章中,笔者提到了反射和特性中有一个MethodInfo的类。这个类在反射中是表示方法的元数据,说的通俗点,它就表示一个方法,只不过这个被表示的方法只能通过MethodInfo类的Invoke方法被调用。既然MehodInfo的对象代表的是方法,那么我们就可以定义一个“命令—方法”键值关系,并保存在字典类中。用“命令”做为“键”,用“方法”作为值。


ok,下面来看代码。由于在这个程序中,我们需要考虑扩展以及多命令,所以还是要使用“特性”和“反射”等技术。

首先,定义一个特性类和命令类,并在命令类中的方法上使用特性。

    /// <summary>
    /// 用于反射的特性
    /// </summary>
    class CMDAttribute : System.Attribute
    {
        string[] cmd;
        public string[] Cmd
        {
            get { return cmd; }
        }

        public CMDAttribute(params string[] cmd)
        {
            this.cmd = cmd;
        }

    }

    /// <summary>
    /// 这是个命令类,供用户调用
    /// </summary>
   static class Commander
    {

        [CMD("s","sh","h","hi")]
       static public int SayHi()
        {
            Console.WriteLine("Hello girl. How beautifull you are!");
            return 0;
        }

        [CMD("i","ip","ipad")]
       static public int Ipad()
        {
            Console.WriteLine("yes i have a Ipad!");
            return 0;
        }

    }
注意:Commander类的定义中使用了static关键字,主要是为了本例的方便。因为如果不使用static关键字,那么在使用这个类的时候,还必须先创建对象。


现在我们再定义一个类,让这个类的对象被创建的时候,自动加载保存“命令—方法”的字典。代码如下:

   class InitilizeCmd
   {
       Dictionary<string, MethodInfo> cmd;


       public Dictionary<string, MethodInfo> Cmd
       {
           get { return cmd; }
       }


       public void InitilizeCmd()
       {
           CMDAttribute Attri;
           Type TPC = typeof(Commander);
           Type TPA = typeof(CMDAttribute);
           MethodInfo[] meths = TPC.GetMethods();


           //遍历方法数组,获取每个方法的特性,然后将方法的特性与用户输入的命令比较。如果用户输入的命令与特性一致,就执行这个特性所表示的方法,如果不一致,就告诉用户命令有错。
           foreach (MethodInfo M in meths)
           {
               //判断方法是否应用了TCmdAttribute所抽象的特性。并且不向上查找继承的类。  
               //这一句非常重要。因为Type的GetMethods()方法会获取类的所有方法,包括他所继承的基类的方法。  
               //而基类的方法是没有使用我们自定义的特性,那么在进行下一句,对attr赋值是,attr会是一个空。  
               //所有我们使用MethodInfo对象的IsDefined()方法来确定某个方法使用了某个自定义的特性。  
               if (M.IsDefined(TPA, false))
               {
                   //获取方法的特性。  
                   Attri = (CMDAttribute)M.GetCustomAttribute(TPA, false);


                   //将方法的特性和方法名作为一个键—值关系存入Dictionary<string, int>字典中。
                   foreach (string commad in Attri.Cmd)
                   {
                       cmd.Add(commad, M);
                   }
               }
           }
       }
   }


到目前为止,我们的命令类有了,利用特性和反射将“命令(特性值)”和“方法”保持至字典的类也有了。现在要使用这个类,只需要创建InitilizeCmd类的对象就可以了。

那么在程序的Main()函数中我们就可以这样写了:

        static void Main(string[] args)
        {
            //创建InitilizeCmd类的对象,并初始化。
            InitilizeCmd INCMD = new InitilizeCmd();

            //创建MethodInfo对象
            MethodInfo M;
            do
            {
                Console.WriteLine("请输入命令...");
                string InputCMD = Console.ReadLine().ToLower();
                try
                {
                    M = INCMD.Cmd[InputCMD];
                    M.Invoke(null, null);

                }
                catch (Exception e)
                {
                    Console.WriteLine("命令输入有误,请重新输入\n"+e.Message);
                }
            } while (true);
        }


 
在Main()函数中使用try……catch结构是因为如果用户输入的命令不正确,会导出无法查找字典中键—值,使得程序出错。

此致,将“字典”与“特性”、“反射”相结合的方法就介绍完了。这样做的好处显而易见,只在加载程序时利用反射遍历一次方法数组,而无需在输入命令时一遍又一遍的遍历数组。这样既保证了程序的扩展性,也保证了大大提高了程序运行的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值