一、簡介
控制台程序小巧、便捷,開發起來簡單。一般,我寫項目時習慣在原定客戶端之外,寫一個控制台的客戶端。這樣有幾個好處:
(1)開發量較Web或GUI少得多。
(2)運行起來簡單,占有資源很少。
(3)便於跟蹤程序的運行。比如,用 log4net 記錄日志的話,將appender-ref設置成ConsoleAppender,可以清楚看清系統運行軌跡,在使用nhibernate/activerecord開發時尤其方便。
(4)當為同一個系統開發兩種不同的UI時,會自覺的做好分層,這樣可以使系統的層次結構更清晰,便於維護。
然而,雖然控制台程序的開發量少,也還是有一些常用功能實現起來較繁瑣。比如,以下幾個問題:
(1)輸入密碼。用戶輸入密碼時,控制台顯示****而不是密碼明文;
(2)指令的解析與分派。控制台中,經常需要向程序輸入純字符串格式的指令,解析指令,解析參數的個數,調用相應的方法。
(3)指令的幫助系統。顯示全部指令及其介紹。
(4)指令的自動補全。
這幾個問題在寫控制台程序上經常會碰到,為此我寫了兩個類 ConsoleUtil 和 CmdDispatcher,實現了上述功能,以供復用。於此下載代碼。
代碼是C#3.0 寫的,若要用在其它C#版本,需要做一定的改動。
二、使用方式:
(1)輸入密碼
調用靜態方法String ConsoleUtil.ReadPassword(String msg, String errMsgOnNull) 獲取輸入的密碼。
(2)指令的解析、分派、自動補全與幫助系統
(a)創建一個 CmdDispatcher 對象。
(b)使用CmdDispatcher對象的AddCmdFunc方法,加入指令委托。這裡定義了五種委托:
delegate void Func0(); delegate void Func1(String s1); delegate void Func2(String s1, String s2); delegate void Func3(String s1, String s2, String s3); delegate void Func4(String s1, String s2, String s3, String s4);
AddCmdFunc方法有兩種使用方式。
AddCmdFunc(String cmd, Func0|Func1|Func2|Func3|Func4 func)
和
AddCmdFunc(String cmd, String argsString, String introduce, Func0|Func1|Func2|Func3|Func4 func)
後一種方式中 argsString 是該指令的參數字符串,introduce 是對這個指令的介紹。這兩個變量的唯一意義是顯示在該指令的help信息之中。如果使用前一種方式,該指令的help信息便是光禿禿的。
比如,
CmdDispatcher cd = new CmdDispatcher();
cd.AddCmdFunc("help", "無參數", "查詢幫助.",
() => { cd.PrintHelp(); });
cd.AddCmdFunc("cmd1", "無參數", "指令cmd1.",
() => { Console.WriteLine(String.Format("Invoke cmd1.")); });
cd.AddCmdFunc("cmd2", "arg0 arg1", "指令cmd1.",
(arg0, arg1) => { Console.WriteLine(String.Format("Invoke cmd1({0},{1}).", arg0, arg1)); });
cd.AddCmdFunc("cmd3",
(arg0, arg1, arg2) => { Console.WriteLine(String.Format("Invoke cmd1({0},{1},{2}).", arg0, arg1, arg2)); });
顯示出來的 help 信息為:
help 無參數
查詢幫助.
cmd2 arg0 arg1
指令cmd1.
cmd3 無參數
(3)通過CmdDispatcher對象的String ReadlineWithIntelliSence()方法獲取控制台輸入的指令.通過CmdDispatcher對象的Handle(String input)方法便可解析指令,分派給相應的委托完成。
舉例:
while (true)
{
Console.Write(cd.Prefix); // 在控制台上輸出提示符 >>。
String input = cd.ReadlineWithIntelliSence();
cd.Handle(input);
}
(4)不匹配的指令的處理方法
CmdDispatcher有一個屬性,public Func0 DefaultFunc { get; set; } 。當CmdDispatcher 找不到匹配的委托時,便調用這個delegate。你可以自行設置 DefaultFunc,否則則用默認的內置 delegate。
三、一個完整的例子
下面是一個完整的例子:
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5
6namespace ConsoleTest
7{
8 class Program
9 {
10 static Boolean EXIT = false;
11 static void Main(string[] args)
12 {
13 String id = ConsoleUtil.Readline("請輸入帳號:","帳號不能為空.");
14 String pwd = ConsoleUtil.ReadPassword("請輸入密碼:","密碼不能為空.");
15 Console.WriteLine("歡迎你,"+ id + "!");
16 CmdDispatcher cd = CreateDispatcher();
17 while (true)
18 {
19 Console.Write(cd.Prefix);
20 String input = cd.ReadlineWithIntelliSence();
21 cd.Handle(input);
22 if (EXIT) return;
23 }
24 }
25
26 static CmdDispatcher CreateDispatcher()
27 {
28 CmdDispatcher cd = new CmdDispatcher();
29 cd.AddCmdFunc("help", "無參數", "查詢幫助.",
30 () => { cd.PrintHelp(); });
31 cd.AddCmdFunc("help", "cmd", "查詢指定指令的幫助.",
32 (cmd) => { cd.PrintHelp(cmd); });
33 cd.AddCmdFunc("exit","無參數","退出程序.",
34 () => { EXIT = true; });
35 cd.AddCmdFunc("cmd1", "無參數", "指令cmd1.",
36 () => { Console.WriteLine(String.Format("Invoke cmd1.")); });
37 cd.AddCmdFunc("cmd1", "arg0", "指令cmd1.",
38 (arg0) => { Console.WriteLine(String.Format("Invoke cmd1({0}).", arg0)); });
39 cd.AddCmdFunc("cmd2", "arg0 arg1", "指令cmd1.",
40 (arg0, arg1) => { Console.WriteLine(String.Format("Invoke cmd1({0},{1}).", arg0, arg1)); });
41 cd.AddCmdFunc("cmd3",
42 (arg0, arg1, arg2) => { Console.WriteLine(String.Format("Invoke cmd1({0},{1},{2}).", arg0, arg1, arg2)); });
43 return cd;
44 }
45 }
46}
47
運行結果:

本文配套源碼