在代码进行优化的时候,发现了switch
case太长,有的竟然长达30个远远超过一屏这样在代码的可读性来说很差。特别在我们看代码的时候要拉下拉框我个人觉得这是不合理的。但是我不建议有
switch就进行反射或委托来解决。看实际的情况比喻10个以为还是可以接受的。因为switch看起来更加的直接而且效率相对来说是最好的。那下面就用代码来一点点进行解释
1:传统的用法
1.1:现在我们有一个需求通过传递参数来获取相关的信息。首先我们先看方法
public class SwitchMethod {
public string GetSerialNumber(string serialNumber)
{
return serialNumber;
}
public string GetName(string name)
{
return name;
}
public string GetAge(string age)
{
return age;
}
public string GetBirthday(string birthday)
{
return birthday;
}
}
1.2:客户端的调用
string action =Console.ReadLine() ;
var switchMethod=new SwitchMethod();
switch (action)
{
case "serialNumber":
Console.WriteLine(switchMethod.GetSerialNumber("1234"));
break;
case "name":
Console.WriteLine(switchMethod.GetName("zhangsan"));
break;
case "age":
Console.WriteLine(switchMethod.GetAge("21"));
break;
case "birthday":
Console.WriteLine(switchMethod.GetBirthday("19960201"));
break;
}
1.3:效果
以上是我们最常规的用法看起来最直观但是你想过没有如果有30个方法呢你还这样进行switch case吗 50,100个呢所以下面我用委托来代码
2:委托替代switch
上面我又发现一个问题action凌乱,如果太多了就搞不清什么是什么了所以我们加入枚举
2.1:建立枚举
public enum ActionEnum
{
/// <summary>
/// 编号
/// </summary>
SerialNumber = 0,
/// <summary>
/// 姓名
/// </summary>
Name = 1,
/// <summary>
/// 年龄
/// </summary>
Age = 2,
/// <summary>
/// 生日
/// </summary>
Birthday = 3
}
2.2:我采取字典把需要switch的都存起来
private static void LoadDictionary()
{
if (AllDictionary.Count<=0)
{
var switchMethod = new SwitchMethod();
AllDictionary.Add(ActionEnum.SerialNumber, switchMethod.GetSerialNumber);
AllDictionary.Add(ActionEnum.Age, switchMethod.GetAge);
AllDictionary.Add(ActionEnum.Birthday, switchMethod.GetBirthday);
AllDictionary.Add(ActionEnum.Name, switchMethod.GetName);
}
}
2.3:建立委托(这是比较简单的其实在方法中还可以提取相似的操作放在委托执行)
public static string Exec(string str,Func<string, string> method) {
return method(str);
}
2.4:客户端调用
Console.WriteLine(Exec("21", AllDictionary[ActionEnum.Age]));
2.5:效果
3:反射替代switch
3.1建立一个自定义Attribute类(目的是为了附带方法中的信息)
public class MethodAttribute : Attribute
{
public ActionEnum MethodName;
public MethodAttribute(ActionEnum methodName)
{
this.MethodName = methodName;
}
}
3.2:定义一个基类
public class BaseMethod
{
public Hashtable GetMethodAttribute<T>(T t)
{
var hashtable = new Hashtable();
Type type = t.GetType();
foreach (MethodInfo method in type.GetMethods())
{
var methodArray = (MethodAttribute[]) method.GetCustomAttributes(typeof (MethodAttribute), false);
foreach (MethodAttribute actionMethodAttribute in methodArray)
{
ActionEnum actionName = actionMethodAttribute.MethodName;
hashtable.Add(actionName, method);
}
}
return hashtable;
}
public string DoAction(ActionEnum actionName,string str) {
Hashtable ht = GetMethodAttribute(this);
string message = ht.Contains(actionName)
? ((MethodInfo) ht[actionName]).Invoke(this, new object[] {str}).ToString()
: string.Format("{0} 超过范围", actionName);
return message;
}
}
3.3:修改SwitchMethod类并给方法加上特性
public class SwitchMethod : BaseMethod
{
[Method(ActionEnum.SerialNumber)]
public string GetSerialNumber(string serialNumber)
{
return serialNumber;
}
[Method(ActionEnum.Name)]
public string GetName(string name)
{
return name;
}
[Method(ActionEnum.Age)]
public string GetAge(string age)
{
return age;
}
[Method(ActionEnum.Birthday)]
public string GetBirthday(string birthday)
{
return birthday;
}
}
3.4:客户端调用
string result = new
SwitchMethod().DoAction(ActionEnum.SerialNumber,"1332");
3.5:注释
3.5.1:type.GetMethods():获取这个类中所有的方法包括基类的方法
3.5.2:method.GetCustomAttributes(typeof (MethodAttribute),
false):获取这个方法所有关于MethodAttribute类型的自定义特性
3.5.3:MethodInfo:表示对类中方法的访问
3.6:运行效果
三种方式总结
1:传统的用法
优点:简单易读,效率高
缺点:当量很多的时候会造成方法很长,不易维护,可能修改其中某一个case会引起未知的错误
2:委托
优点:使用委托将公有的进行提取,减少代码量
缺点:加入字典后每次添加都需要在字典后手动添加一个子项。总是觉得别扭,效率稍微差点
3:反射
优点:代码量减少,不在考虑内部如何实现,而且符合开闭原则,只需要添加新的方法,其他地方不作修改。维护性强
缺点:很明显这个效率最差(此处并未加入缓存)
补充 利用反射代替switch
根据传进来不同的值,调用不同的方法
protected void btn_SwitchClick(object sender, EventArgs e)
{
string result = "";
switch (ddlMethod.SelectedValue)
{
case "A":
result = SwitchTest.GetA();
break;
case "B":
result = SwitchTest.GetB();
break;
case "C":
result = SwitchTest.GetC();
break;
default:
result = ddlMethod.SelectedValue + "方法找不到";
break;
}
ltrResult.Text = result;
}
下面利用反射机制实现,首选需要一个自定义属性类
public class ActionMethodAttribute:Attribute
{
public string ActionTypeName;
public ActionMethodAttribute(string typeName)
{
this.ActionTypeName = typeName;
}
}
然后定义一个基类
public abstract class GenericBLL
{
public Hashtable GetMethodAttribute<T>(T t)
{
Hashtable ht = new Hashtable();
Hashtable obj = CacheHandler<Hashtable>.GetCache(t.ToString());
if (obj == null)
{
Type type = t.GetType();
foreach (MethodInfo mi in type.GetMethods())
{
ActionMethodAttribute[] mis = (ActionMethodAttribute[])mi.GetCustomAttributes(typeof(ActionMethodAttribute), false);
foreach (ActionMethodAttribute actionMethodAttribute in mis)
{
string actionName = actionMethodAttribute.ActionTypeName;
ht.Add(actionName, mi);
}
}
CacheHandler<Hashtable>.SetCache(t.ToString(), ht);
}
else
{
ht = (Hashtable)obj;
}
return ht;
}
/// <summary>
/// return message;
/// </summary>
/// <param name="actionName"></param>
/// <returns></returns>
public string DoAction(string actionName)
{
string message;
Hashtable ht = GetMethodAttribute(this);
if (ht.Contains(actionName))
{
message = ((MethodInfo)ht[actionName]).Invoke(this, new object[] { }).ToString();
}
else
{
message = string.Format("{0} Not Defined.!", actionName);
//throw new Exception(errmsg);
}
return message;
}
}
实现类继承,
public class ReflectTest:GenericBLL
{
[ActionMethod("A")]
public string GetA()
{
return "调用的A";
}
[ActionMethod("B")]
public string GetB()
{
return "调用的B";
}
[ActionMethod("C")]
public string GetC()
{
return "调用的C";
}
}
具体的调用
protected void btn_ReflectClick(object sender, EventArgs e)
{
string result = ReflectTest.DoAction(ddlMethod.SelectedValue);
ltrResult.Text = result;
}
ASPX中的代码如下
选D会提示没有D方法
<asp:DropDownList ID="ddlMethod" runat="server">
<asp:ListItem Text="A" Value="A">
</asp:ListItem>
<asp:ListItem Text="B" Value="B">
</asp:ListItem>
<asp:ListItem Text="C" Value="C">
</asp:ListItem>
<asp:ListItem Text="D" Value="D">
</asp:ListItem>
</asp:DropDownList>
<br />
<asp:Button ID="btnInvoke" Text="Switch" OnClick="btn_SwitchClick" runat="server" />
<asp:Button ID="btnInvokeR" Text="Reflect" OnClick="btn_ReflectClick" runat="server" />
<br>
<asp:Literal ID="ltrResult" runat="server" />