在任务跳转问题上,C#的委托delegate与工厂方法模式的异曲同工之妙

在玩游戏时,常常会有任务跳转。但当跳转类型过多时,对游戏研发者来说,就是一件头疼的事了。之前在做游戏城镇等级模块时,就遇到了任务跳转过多的问题。刚接到任务跳转这个需求时,由于入门未深,便用if-else写的。可以想象一百多行的if-else或者switch是多么壮观啊。。。

但是当我们只为了完成当日任务,而不为长远维护考虑,那未来将是一件可怕的事情。而且那样的代码,像我这样的人看着也不舒服。

最近看了C#高级语法,赫然发现C#中的委托不就是C++中的函数指针嘛。于是,对于上述if-else,便有解了。

我们只需要写一个委托,对应创建一个数组对象,每个跳转类型封装一个方法,再用委托来回调,这样的代码,主方法里只需1行就搞定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UICityRichLevelItem : ScreenBaseHandler
{
//public RawImage imgIcon;
public Text textProgress;
public Text textDegreeOfCompletion;
public Slider slideProgress;
public GameObject imgBg;
public GameObject imgCompleteFinish;
public GameObject imgCompleteGoto;
public UI_Event btnGoto;
public GameObject imgCompleteDelivery;
public UI_Event btnDelivery;
private int typeId;
private int taskId;
private bool isDelivery;

private string TaskDescription;
private int allNum;
private int submitId; //需要提交的id
private const int NumOfTaskTypes = 9; //任务种类数量
private const int DeliveryOfGoodsTaskId = 9; //提交货物类型id

private delegate void JumpTask();
JumpTask [] jumpTask = new JumpTask[NumOfTaskTypes];

public override void Init()
{
isDelivery = false;
allNum = 0;
submitId = 0;
btnGoto.onClick = (eventData, ev) => BtnGotoOnClick();
btnDelivery.onClick = (eventData, ev) => BtnDeliveryOnClick();

jumpTask[0] = new JumpTask(JumpTaskType_0);
jumpTask[1] = new JumpTask(JumpTaskType_1);
jumpTask[2] = new JumpTask(JumpTaskType_2);
jumpTask[3] = new JumpTask(JumpTaskType_3);
jumpTask[4] = new JumpTask(JumpTaskType_4);
jumpTask[5] = new JumpTask(JumpTaskType_5);
jumpTask[6] = new JumpTask(JumpTaskType_6);
jumpTask[7] = new JumpTask(JumpTaskType_7);
jumpTask[8] = new JumpTask(JumpTaskType_8);
}

public void SetItem (TableCityRichEvent.Data next)
{
taskId = next.mId;
typeId = next.mFunctionType;
if (next.mFunctionParameter.Count == 1)
{
allNum = next.mFunctionParameter[0];
}
else
{
submitId = next.mFunctionParameter[0];
allNum = next.mFunctionParameter[1];
}
TaskDescription = next.mEventsDes;
textDegreeOfCompletion.text = "0/" + allNum;
textProgress.text = JointStr(TaskDescription, textDegreeOfCompletion.text);
slideProgress.maxValue = allNum;
slideProgress.value = 0;
if (isDeliveryOfGoodsTask())
{
int num = StorageManager.instance.GetAllCommodityUnlockNumById(submitId);
textDegreeOfCompletion.text = num + "/" + allNum;
textProgress.text = JointStr(TaskDescription, textDegreeOfCompletion.text);
slideProgress.value = num > allNum ? allNum : num;
}
isCompleted(allNum, (int)slideProgress.value);
}

public int GetTypeItem()
{
return typeId;
}

public bool HasChecked()
{
return isDelivery;
}

public bool SetCurNum (int curNum)
{
if (!isDeliveryOfGoodsTask())
{
slideProgress.value = curNum;
textDegreeOfCompletion.text = curNum + "/" + allNum;
textProgress.text = JointStr(TaskDescription, textDegreeOfCompletion.text);
}
if (curNum >= allNum)
{
isDelivery = true;
slideProgress.value = curNum;
}
isCompleted(allNum, curNum);
return curNum >= allNum;
}

private string JointStr(string str1, string str2)
{
return str1 + " " + str2;
}

private void isCompleted(int maxx, int cur)
{
if (isDeliveryOfGoodsTask())
{
imgCompleteDelivery.SetActive(!isDelivery);
imgCompleteGoto.SetActive(false);
imgCompleteFinish.SetActive(isDelivery);
}
else
{
imgCompleteDelivery.SetActive(false);
imgCompleteGoto.SetActive(maxx > cur);
imgCompleteFinish.SetActive(maxx <= cur);
}
}

public bool isDeliveryOfGoodsTask()
{
return TaskHash(typeId) == DeliveryOfGoodsTaskId;
}

private void BtnGotoOnClick()
{
jumpTask[TaskHash(typeId)]();
}

public void SetBg(bool flag)
{
imgBg.SetActive(flag);
}

private void BtnDeliveryOnClick()
{
int num = StorageManager.instance.GetAllCommodityUnlockNumById(submitId);
if (isDelivery == false && num >= allNum)
{
TCPNetworkAttribute.Instance.ReqRichlevelSubmit(taskId);
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.Init();
//isDelivery = true;
//SetCurNum(allNum);
}
}

private int TaskHash(int id)
{
return id / 1000;
}

private void JumpTaskType_0() //默认
{
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.Init();
}

private void JumpTaskType_1() //出航
{
if (TaskHash(typeId) == 1)
{
int[] types = {
(int)IntentionType.Fishing,
(int)IntentionType.Fishing,
(int)IntentionType.Merchant,
(int)IntentionType.Explore
}; //捕鱼,战峙,贸易,探索
UISetOut_MainPage.intentionType = (IntentionType)types[typeId % 1000 - 1];
}
UISetOut_MainPage config = MenuManager.instance.CreateMenu<UISetOut_MainPage>();
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_2() //建造
{
UIBuildingHandle config = MenuManager.instance.CreateMenu<UIBuildingHandle>();
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_3() //更路簿
{
UILinkDistrict config = MenuManager.instance.CreateMenu<UILinkDistrict>();
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_4() //易市
{
UIMarketHandle config = MenuManager.instance.CreateMenu<UIMarketHandle>();
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_5() //区域解锁
{
UIUnLockZoneHandle handle = MenuManager.instance.CreateMenu<UIUnLockZoneHandle>();
TableLockZone.Data mTableData = null;
List<TableLockZone.Data> lst_zone_table = TableManager.instance.GetLockZoneAll();
int i = 0;
for (; i < lst_zone_table.Count; i++)
{
mTableData = lst_zone_table[i];
int id = mTableData.mId;
if (LockZoneManager.instance.IsReadyZoneUnlock(id))
{
break;
}
}
if (i < lst_zone_table.Count && mTableData != null)
{
handle.SetZone(mTableData);
handle.OpenScreen();
}
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_6() //船坞
{
UIShipHouseHandle config = MenuManager.instance.CreateMenu<UIShipHouseHandle>();
//config.gameObject.transform.Find("Canvas").GetComponent<Canvas>().sortingOrder = 3;
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_7() //广场
{
UIPiazzaHandle config = MenuManager.instance.CreateMenu<UIPiazzaHandle>();
BuildingControl control = BuildingManager.instance.GetBuildingControlByTableId(12);
config.InitFactory(control);
config.OpenScreen();
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.CloseScreen();
}

private void JumpTaskType_8() //人物等级
{
UIPlayerInfo item = MenuManager.instance.FindMenu<UIPlayerInfo>();
item.Init();
item.OpenScreen(0);
}
}

有没有发现,和设计模式中的工厂方法模式有异曲同工之妙呢。日后维护起来也方便许多。

接下来我们详细介绍下C#中的委托吧。

C# 委托(Delegate)

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

声明委托(Delegate)

委托声明决定了可由该委托引用的方法。委托可指向一个与其具有相同标签的方法。

例如,假设有一个委托:

1
public delegate int MyDelegate (string s);

上面的委托可被用于引用任何一个带有一个单一的 string 参数的方法,并返回一个 int 类型变量。

声明委托的语法如下:

1
delegate <return type> <delegate-name> <parameter list>

实例化委托(Delegate)

一旦声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。例如:

1
2
3
4
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);

下面的实例演示了委托的声明、实例化和使用,该委托可用于引用带有一个整型参数的方法,并返回一个整型值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System;
delegate int NumberChanger(int n);
namespace DelegateAppl{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p) {
num += p; return num;
}
public static int MultNum(int q) {
num *= q; return num;
}
public static int getNum() {
return num;
}
static void Main(string[] args) { // 创建委托实例
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum); // 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum()); Console.ReadKey();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
Value of Num: 35
Value of Num: 175

委托的多播(Multicasting of a Delegate)

委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。”-“ 运算符可用于从合并的委托中移除组件委托。

使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。下面的程序演示了委托的多播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;
delegate int NumberChanger(int n); namespace DelegateAppl
{
class TestDelegate
{
static int num = 10; public static int AddNum(int p) { num += p; return num; }
public static int MultNum(int q) { num *= q; return num; }
public static int getNum() { return num; }
static void Main(string[] args)
{ // 创建委托实例
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1; nc += nc2; // 调用多播
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

1
Value of Num: 75

委托(Delegate)的用途

下面的实例演示了委托的用法。委托 printString 可用于引用带有一个字符串作为输入的方法,并不返回任何东西。

我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.IO;
namespace DelegateAppl
{
class PrintString
{
static FileStream fs; static StreamWriter sw; // 委托声明
public delegate void printString(string s); // 该方法打印到控制台
public static void WriteToScreen(string str) {
Console.WriteLine("The String is: {0}", str);
} // 该方法打印到文件
public static void WriteToFile(string s) {
fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs); sw.WriteLine(s); sw.Flush(); sw.Close(); fs.Close();
} // 该方法把委托作为参数,并使用它调用方法
public static void sendString(printString ps) {
ps("Hello World");
}
static void Main(string[] args) {
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1); sendString(ps2);
Console.ReadKey();
}
}
}

当上面的代码被编译和执行时,它会产生下列结果:

1
The String is: Hello World

文章结束了,但我们的故事还在继续
坚持原创技术分享,您的支持将鼓励我继续创作!