用反射(Reflection)和委托(delegate)分发 switch 操作

有时候,我们经常需要处理一些比较大的 switch 语句,如:

public interface IAction
{
    void DoAction(string ActionName);
}
public class OkCancelAction : IAction
{
    public void DoAction(string ActionName)
    {
        switch( ActionName )
        {
            case "OK":
                Console.WriteLine("OK Action raised.");
                break;
            case "CANCEL":
                Console.WriteLine("CANCEL Action raised.");
                break;
            default:
                throw new Exception("ActionName Not Defined.");
        }
    }
}

一般的,如果这个 switch 比较大,可以使用如下的方法进行分发:
public interface IAction
{
    void DoAction(string ActionName);
}
public class OkCancelAction : IAction
{
    public void DoAction(string ActionName)
    {
        switch( ActionName )
        {
            case "OK":
                Ok_Clicked();
                break;
            case "CANCEL":
                Cancel_Clicked();
                break;
            default:
                throw new Exception("ActionName Not Defined.");
        }
    }
    private void Ok_Clicked()
    {
        Console.WriteLine("OK Action raised.");
    }
    private void Cancel_Clicked()
    {
        Console.WriteLine("CANCEL Action raised.");
    }
}


  在我前面的文章中,分别介绍了用反射分发 switch 操作和用委托分发 switch 操作的方法,在其中,我使用了一个 Hashtable 和一个 ArrayList,不过,对于这种总项目数很少,而且要保持插入顺序的问题,使用 ListDirectory 更为合适,一来只要它一个就可以了,二来它的内存占用也比 Hashtable 小得多。

  使用反射,可以把所有的操作都隐藏在基类中;而使用委托,可以用一种类型安全的方式进行函数调用。所以找到一种方法,结合反射和委托,来处理这个问题:

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
namespace ActionSwitcher
{
    //======================================
    class ApplicationEntry
    {
        [STAThread]
        static void Main(string[] args)
        {
            ShowAction( new OkCancelAction() );
            Console.WriteLine("/n");
            ShowAction( new SaveLoadAction() );
            Console.ReadLine();
        }
        private static void ShowAction(ActionBase a)
        {
            foreach ( string s in a.GetActionNames() )
            {
                Console.WriteLine("// {0}", s);
                a.DoAction(s);
            }
        }
    }
    //======================================
    public class ActionNameNotDefinedException : Exception
    {
        public ActionNameNotDefinedException() : base("Action Name Not Defined.") {}
        public ActionNameNotDefinedException(string ErrorMessage) : base(ErrorMessage) {}
    }
    //======================================
    [AttributeUsage(AttributeTargets.Method)]
    public class ActionMethodAttribute : Attribute
    {
        private string _ActionName;
        public string ActionName
        {
            get { return _ActionName; }
        }
        public ActionMethodAttribute(string ActionName)
        {
            _ActionName = ActionName;
        }
    }
    //======================================
    public delegate void ActionMethodDelegate();
    //======================================
    public abstract class ActionBase
    {
        private ListDictionary ld = new ListDictionary();
        public string [] GetActionNames()
        {
            ICollection ic = ld.Keys;
            string [] sRet = new string[ic.Count];
            ic.CopyTo(sRet, 0);
            return sRet;
        }
        public ActionBase()
        {
            Type t = this.GetType();
           
            foreach ( MethodInfo mi in t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic) )
            {
                ActionMethodAttribute[] amas = (ActionMethodAttribute[])mi.GetCustomAttributes(typeof(ActionMethodAttribute), false);
                if ( amas != null && amas.Length > 0 )
                {
                    string ActionName = amas[0].ActionName;
                    Delegate amd = System.Delegate.CreateDelegate(typeof(ActionMethodDelegate), this, mi.Name);
                    ld.Add( ActionName, amd );
                }
            }
        }
        public void DoAction(string ActionName)
        {
            ActionMethodDelegate amd = (ActionMethodDelegate)ld[ActionName];
            if ( amd != null )
            {
                amd();
            }
            else
            {
                throw new ActionNameNotDefinedException();
            }
        }
    }
    //======================================
    public class OkCancelAction : ActionBase
    {
        [ActionMethod("OK")]
        private void Ok_Clicked()
        {
            Console.WriteLine("OK Action raised.");
        }
        [ActionMethod("CANCEL")]
        private void Cancel_Clicked()
        {
            Console.WriteLine("CANCEL Action raised.");
        }
    }
    //======================================
    public class SaveLoadAction : ActionBase
    {
        [ActionMethod("SAVE")]
        private void Save_Clicked()
        {
            Console.WriteLine("SAVE Action raised.");
        }
        [ActionMethod("LOAD")]
        private void Load_Clicked()
        {
            Console.WriteLine("LOAD Action raised.");
        }
    }
    //======================================
}

  这种操作有一个基类(或者接口),说明可以应用 Command 模式,只不过其内部根据输入值的不同,又可以做成 Command 模式,这样,对于很多 Action 的情况,细小的类就会过多,所以采用分发 switch 的操作方法。概括来说,对于小于 10 个操作,而且每一个操作都比较单纯的情况,而又有许多这种 操作组的情况,使用这种方式比较合适,不过,对于比较复杂的情况,还是应该使用 Command 模式,关于 Command 模式,可以参考我在“点睛简单脚本引擎”中的做法,这样,才能使程序的演化不至于造成很多混乱的代码,避免造成维护的困难。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值