在开发 asp.net 项目中,通常使用一般处理程序(ashx)处理前端发送过来的请求,因为一个handler会处理多个请求,故ajax请求中一般都会加一个action的参数,在handler里根据这个action做相应的处理或返回相应的数据,这里大多数人都会想到用switch...case做判断,一开始我也是用的switch,但渐渐地发现,每个case不像一个代码块,不能为其中的变量提供一个独立的作用域!而且,如果case的情况比较多的话,代码看上去也比较臃肿难维护;
那如何替换掉switch...case呢,我想到了如下两个方案:

1、用委托字典代替switch...case;

首先在handler里声明一个私有的静态委托字典,key为action字符串,value为Func委托;然后把action和对应的方法添加到字典中即可;
完整示例代码:

namespace WebApplication1
{

    public class Handler1 : IHttpHandler
    {
        static Dictionary<string, Action<HttpContext>> MapActions = new Dictionary<string, Action<HttpContext>>(StringComparer.OrdinalIgnoreCase)
        {
            {"Add", Add},
            {"Sub", Sub}
        };

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            try
            {
                var action = context.Request["Action"];
                if (string.IsNullOrEmpty(action))
                {
                    context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
                }

                if (MapActions.ContainsKey(action))
                {
                    var actionFun = MapActions[action];
                    if (actionFun != null)
                    {
                        actionFun(context);
                        //或
                        //actionFun.Invoke(context);
                    }
                    else
                    {
                        context.Response.StatusCode = (int) HttpStatusCode.NotImplemented;
                    }
                }
                else
                {
                    context.Response.StatusCode = (int) HttpStatusCode.NotFound;
                }
            }
            catch (Exception e)
            {
                context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
            }
            finally
            {
                context.Response.End();
            }
        }

        public static void Add(HttpContext context)
        {
            int num1 = int.Parse(context.Request["Num1"]);
            int num2 = int.Parse(context.Request["Num2"]);
            int result = num1 + num2;
            context.Response.Write(result);
        }

        public static void Sub(HttpContext context)
        {
            int num1 = int.Parse(context.Request["Num1"]);
            int num2 = int.Parse(context.Request["Num2"]);
            int result = num1 - num2;
            context.Response.Write(result);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

2、利用反射替代switch...case;

利用反射,将action的值与具体的方法对应上;
完整示例代码:

namespace WebApplication1
{

    public class Handler2 : IHttpHandler
    {
        static readonly Type[] SearchParamType = new[] { typeof(HttpContext) };
        public void ProcessRequest(HttpContext context)
        {
            var result = ActionInvoke(context);

            context.Response.ContentType = "text/plain";
            context.Response.Write(result);
        }
        private object ActionInvoke(HttpContext ctx)
        {
            var actionFun = this.GetType().GetMethod("ProcessAction_" + ctx.Request["action"] ?? "",
                BindingFlags.NonPublic |
                BindingFlags.IgnoreCase |
                BindingFlags.Instance |
                BindingFlags.Public,
                null,
                SearchParamType,
                null);
            if (null == actionFun)
            {
                return "UnknowAction";
            }
            return actionFun.Invoke(this, new[] { ctx });
        }
        public int ProcessAction_Add(HttpContext context)
        {
            int num1 = int.Parse(context.Request["Num1"]);
            int num2 = int.Parse(context.Request["Num2"]);
            int result = num1 + num2;
            return result;
        }

        public int ProcessAction_Sub(HttpContext context)
        {
            int num1 = int.Parse(context.Request["Num1"]);
            int num2 = int.Parse(context.Request["Num2"]);
            int result = num1 - num2;
            return result;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

3、比较两种方案

反射会造成一定的性能损耗;所以使用委托字典方案更加靠谱;

4、其他方案

可以使用设计模式实现,比如工厂模式,状态模式,中介模式等,但用在上述这个场景,那么就有杀鸡用牛刀的 感觉了;

5、说明

在简单的逻辑中,case分支少,很少扩展,那么应该用switch语句,因为简单明了,易于阅读。
如果在复杂的逻辑中,复杂而且混乱的case,而且经常扩展什么的,就应该用委托和反射,使用缓存机制。

6、参考

重构:switch语句改成策略模式还是状态模式:https://blog.csdn.net/qq_21381465/article/details/51298808
中介模式: https://www.cnblogs.com/insus/p/4134383.html


08-02 09:34