有时候,我们经常需要处理一些比较大的 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);
}
{
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.");
}
}
{
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.");
}
{
Console.WriteLine("OK Action raised.");
}
private void Cancel_Clicked()
{
Console.WriteLine("CANCEL Action raised.");
}
}
{
Console.WriteLine("CANCEL Action raised.");
}
}
在我前面的文章中,分别介绍了用反射分发 switch 操作和用委托分发 switch 操作的方法,在其中,我使用了一个 Hashtable 和一个 ArrayList,不过,对于这种总项目数很少,而且要保持插入顺序的问题,使用 ListDirectory 更为合适,一来只要它一个就可以了,二来它的内存占用也比 Hashtable 小得多。
使用反射,可以把所有的操作都隐藏在基类中;而使用委托,可以用一种类型安全的方式进行函数调用。所以找到一种方法,结合反射和委托,来处理这个问题:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
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();
}
{
//======================================
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;
{
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; }
}
{
get { return _ActionName; }
}
public ActionMethodAttribute(string ActionName)
{
_ActionName = ActionName;
}
}
//======================================
public delegate void ActionMethodDelegate();
//======================================
public abstract class ActionBase
{
private ListDictionary ld = new ListDictionary();
{
_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;
}
{
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 );
}
}
}
{
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.");
}
{
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.");
}
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.");
}
}
//======================================
}
private void Load_Clicked()
{
Console.WriteLine("LOAD Action raised.");
}
}
//======================================
}
这种操作有一个基类(或者接口),说明可以应用 Command 模式,只不过其内部根据输入值的不同,又可以做成 Command 模式,这样,对于很多 Action 的情况,细小的类就会过多,所以采用分发 switch 的操作方法。概括来说,对于小于 10 个操作,而且每一个操作都比较单纯的情况,而又有许多这种 操作组的情况,使用这种方式比较合适,不过,对于比较复杂的情况,还是应该使用 Command 模式,关于 Command 模式,可以参考我在“点睛简单脚本引擎”中的做法,这样,才能使程序的演化不至于造成很多混乱的代码,避免造成维护的困难。