[.NET] : 遞迴委派
前言 :
遞迴(Recursion) : 是一種在設計程式時會用的手法,讓函式自己呼叫自己形成迴圈,完成一些複雜的工作。
例如底下的遞迴函式,可以印出排成三角形的星星。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Recursion(0);
Console.ReadLine();
}
static void Recursion(int count)
{
if (count > 10) return;
count++;
Print(count);
Recursion(count);
Print(count);
}
static void Print(int count)
{
for (int i = 0; i < count; i++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
委派(Delegate) : 是 Command Pattern的延伸,主要是將函式做封裝。
例如在.NET裡物件的Event是使用委派來實作,多執行緒的程式也可以採用委派來做設計。
相關範例 : [Windows Forms] : 跨執行緒控制WinForm表單物件
最近在開發專案的時候,需要使用到擁有遞迴能力的委派。一開始以為是個簡單的工作,卻發現過程卻要花一些心思。
本篇文章示範,如何將遞迴跟委派做結合,讓委派也擁有遞迴的能力。
除了給自己做紀錄之外,也希望能將知識分享給有需要的開發人員。
實作 :
首先很直覺的將遞迴函式的範例,改寫成下列的程式碼。
將遞迴函式 Recursion(int count)使用匿名函式的方式封裝成委派。
但這樣的程式碼是無法通過編譯,因為在匿名函式的內部沒有辦法參考到自己。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Action<int> recursionDelegate = delegate(int count)
{
if (count > 10) return;
count++;
Print(count);
recursionDelegate(count);
Print(count);
};
recursionDelegate(0);
Console.ReadLine();
}
static void Print(int count)
{
for (int i = 0; i < count; i++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
換個解法,既然問題是沒有辦法參考到自己,那就把自己傳進去。
以這樣的思路,寫出了下列的程式碼。
但這樣的程式碼還是無法通過編譯,因為委派宣告的引數已經被改變。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Action<Action<int>, int> recursionDelegate = delegate(Action<int> selfDelegate, int count)
{
if (count > 10) return;
count++;
Print(count);
selfDelegate(count);
Print(count);
};
recursionDelegate(recursionDelegate, 0);
Console.ReadLine();
}
static void Print(int count)
{
for (int i = 0; i < count; i++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
最後,為了不被自己的委派宣告引數所限制,在傳遞自己的時候改用object類型做傳遞,要使用時再做轉型。
以這樣的思路,寫出了下列的程式碼。
這樣的程式碼通過了系統的編譯,並且執行結果也跟一開始的遞回函式範例相同。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Action<object, int> recursionDelegate = delegate(object selfDelegate, int count)
{
if (count > 10) return;
count++;
Print(count);
(selfDelegate as Action<object, int>)(selfDelegate, count);
Print(count);
};
recursionDelegate(recursionDelegate, 0);
Console.ReadLine();
}
static void Print(int count)
{
for (int i = 0; i < count; i++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
補充 :
感謝 Lastsecret補充說,也可以使用匿名方法的Outer變數來傳遞委派。
以這樣的思路,寫出了下列的程式碼。
這樣的程式碼通過了系統的編譯,並且執行結果也跟一開始的遞回函式範例相同。
相關資料 : 匿名方法 (C# 程式設計手冊)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Action<int> recursionDelegate = null;
recursionDelegate = delegate(int count)
{
if (count > 10) return;
count++;
Print(count);
recursionDelegate(count);
Print(count);
};
recursionDelegate(0);
Console.ReadLine();
}
static void Print(int count)
{
for (int i = 0; i < count; i++)
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。