可以理解爲 把方法當成方法的參數
public void HelloWorld(string name, 方法 方法名)
C#的委託可理解爲函數的一個包裝,使得C#中的函數可以作爲參數進行傳遞,作用上相當於C/C++中的函數指針。如果函數指針想要指向某函數,函數指針的返回值類型和指向的函數的返回值類型必須相同,並且參數相同。
委託delegate是函數指針的升級版,函數指針是C/C++語言中特有的功能。
$ vim delegate.c
#include <stdio.h>
#include <stdlib.h>
//定義函數指針類型
typedef int(*Calc)(int x, int y);
int add(int x, int y)
{
return x+y;
}
int sub(int x, int y){
return x-y;
}
int main()
{
int x = 100;
int y = 200;
int z = 0;
//函數調用,直接調用。
z = add(x,y);
printf("%d + %d = %d\n", x, y, z);
z = sub(x,y);
printf("%d - %d = %d\n", x, y, z);
//定義函數指針,間接調用。
Calc fnp1 = &add;
Calc fnp2 = ⊂
z = fnp1(x, y);
printf("%d + %d = %d\n", x, y, z);
z = fnp2(x, y);
printf("%d + %d = %d\n", x, y, z);
system("pause");
return 0;
}
$ gcc delegate.c -o delegate.exe
$ delegate.exe
100 + 200 = 300
100 - 200 = -100
100 + 200 = 300
100 + 200 = -100
定義委託:
使用委託首先要定義委託,聲明委託能代理什麼類型的方法,類似房產中介能代理抵押貸款業務,而不能代理打官司一樣。委託的定義和方法的定義類似,只是在定義前多了一個delegate關鍵字。
定義委託的語法:
<訪問修飾符> delegate 返回類型 委託名稱();
示例:
public delegate void DelagateName(int intParam, string strParam)
委託能包裝的方法有一定限制,委託類型DelegateName包裝的方法需滿足條件
(1)方法的返回值類型必須爲void。
(2)方法必須有兩個參數,第一個參數必須爲int類型,第二個參數必須爲string類型。
聲明委託:
如果要把方法當做參數來傳遞的話,就要用到委託。簡單來說,委託就是一個類型,這個類型可以賦值一個方法的引用。
C#中使用類分爲兩個階段,首先定義類,即告訴編譯器這個類由什麼字段和方法組成,然後使用類實例化後的對象。在使用委託時,也需要經歷兩個階段,首先定義委託,即告知編譯器這個委託可以指向那些類型的方法,然後創建該委託的實例。
使用delegate定義委託,定義委託需定義所要指向方法的參數和返回值。
//定義叫IntMethodInvoker的委託,指向帶有int類型參數的方法且返回值爲void。
delegate void IntMethodInvoker(int x);
delegate double TwoLongOp(long first, long second);
delegate string GetString();
使用委託:
using System;
namespace CSharp{
class Program{
//聲明委託:定義委託類型,委託類型名稱爲GetString
private delegate string GetString();
//入口主函數
static void Main(string[] args){
//使用委託調用ToString方法
int x = 100;
//聲明委託:使用委託類型創建實例
//GetString method 使用委託聲明一個類型叫做method
//new GetString(x.ToString)使用new對其進行初始化,使其引用到x中的ToSSString ()上,這樣method就相當於x.ToString
//ToString方法用來把數據轉換成字符串
//GetString method = new GetString(x.ToString);
//初始化委託:直接將方法賦值爲委託類型的變量
GetString methed = x.ToString;
//通過委託實例調用方法,通過Invoke方法調用method所引用的方法
//string result = method.Invoke();
//通過委託實例調用方法 簡寫
//通過method()執行方法就相當於x.ToString()
string result = method();
Console.WriteLine(result);
//100
Console.ReadKey();
}
}
}
using System;
namespace CSharp
{
class Program
{
/*定義委託*/
private delegate void PrintString();
/*使用委託作爲方法的參數*/
static void PrintMethod(PrintString print)
{
print();
}
//靜態方法
static void Method1()
{
Console.WriteLine("Method1");
}
//靜態方法
static void Method2()
{
Console.WriteLine("Method2");
}
//入口主函數
static void Main(string[] args)
{
//定義委託變量並初始化
PrintString method = Method1;
//將委託變量作爲方法參數
PrintMethod(method); //Method1
method = Method2;
PrintMethod(method); //Method2
Console.ReadKey();
}
}
}
綜合案例
using System;
namespace Test
{
class Operate
{
/*數組按降序排序*/
public static bool ArraySort(int[] arr)
{
for(int i=arr.GetUpperBound(0); i>=0; i--)
{
for(int j=0; j<i; j++)
{
if(arr[j] <= arr[i])
{
Swap(ref arr[j], ref arr[i]);
}
}
}
return true;
}
/*交換兩個變量的值*/
public static void Swap(ref int x, ref int y)
{
int tmp = x;
x = y;
y = tmp;
}
}
class Program
{
/*定義委託*/
public delegate bool DelegateSort(int[] arr);
static void Main(string[] args)
{
int[] arr = new int[] { 3, 2, 9, 2, 4, 1 };
Console.WriteLine("排序前");
foreach (int i in arr)
{
Console.WriteLine(i);
}
//聲明委託變量
DelegateSort delegateSort;
//實例化委託,委託Operate的ArraySort排序
delegateSort = new DelegateSort(Operate.ArraySort);
//傳遞參數,調用委託進行排序
delegateSort(arr);
Console.WriteLine("排序後");
foreach (int i in arr)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
}
}
一切皆地址,程序的本質是數據+算法。
(1)變量(數據)是以某個地址爲起點的一段內存中所存儲的值
(2)函數(算法)是以某個地址爲起點的一段內存中所存儲的一組機器語言指令
函數的直接調用和間接調用
直接調用:
通過函數名來調用函數,CPU通過函數名直接獲得函數所在地址並開始執行並返回。
間接調用:
通過函數指針來調用函數,CPU通過讀取函數指針存儲的值來獲取函數所在地址並開始執行並返回。
Java中沒有與委託相對應的功能
預定義委託類型
(1)Action 委託
(2)Func 委託
除了自定義的委託之外,系統給我們提供了內置的委託類型Action和Func。
Action委託是系統內置預定義的,無返回值的泛型類型。其方法可通泛型來實現。
using System;
namespace CSharp{
class Program{
//無參數且無返回值的方法
static void Print(){
Console.WriteLine("hello");
}
//入口主函數
static void Main(string[] args){
//Action是系統預定義的一個委託類型
//Action指向無參數且無返回值的方法
Action action = Print;
action();
Console.ReadKey();
}
}
}
Action委託引用了一個返回值爲void的方法,其中T表示方法參數。
Action
Action<in T>
Action<in T1, in T2>
Action<in T1, in T2... in T6>
using System;
namespace CSharp
{
class Program
{
//無參數且無返回值的方法
static void Print()
{
Console.WriteLine("hello");
}
//有參數無返回值的方法
static void Print(string message)
{
Console.WriteLine(message);
}
static void Print(int x, int y)
{
Console.WriteLine(x+y);
}
//入口主函數
static void Main(string[] args)
{
//Action是系統預定義的一個委託類型,默認指向無參數且無返回值的方法。
Action action = Print;
action();//hello
//泛型委託:定義委託類型,此類型可以指向一個沒有返回值,但帶有一個參數的方法。
Action<string> act = Print;
act("world");//world
//Action可通過泛型T去指定 Action指向的方法的多個參數的類型,參數類型跟action後面聲明的委託類型需保持一致。
Action<int, int> a = Print;
a(100, 200);
Console.ReadKey();
}
}
}
Func委託引用了一個帶有返回值的方法,可傳遞0或多達16個參數類型,和一個返回類型。
Func<out TResult>
Func<in T, out TResult>
Func<in T1, in T2... in T16, out TResult>
using System;
namespace Test
{
class Calculator
{
public void Report()
{
Console.WriteLine("I have 3 methods");
}
public int Add(int x, int y)
{
return x + y;
}
public int Sub(int x, int y)
{
return x - y;
}
}
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
//直接調用
calculator.Report();
//Action委託類型
Action action = new Action(calculator.Report);
//間接調用
action.Invoke();
//簡寫
action();
//Function泛型委託
Func<int, int, int> fn1 = new Func<int, int, int>(calculator.Add);
Func<int, int, int> fn2 = new Func<int, int, int>(calculator.Sub);
int x = 100;
int y = 200;
int z = 0;
//z = fn1.Invoke(x, y);
z = fn1(x, y);
Console.WriteLine("{0} + {1} = {2}", x, y, z);
//z = fn2.Invoke(x, y);
z = fn2(x, y);
Console.WriteLine("{0} - {1} = {2}", x, y, z);
Console.ReadKey();
}
}
}
委託的聲明(自定義委託)
(1)委託是一種類class,類是數據類型,所以委託也是一種數據類型
Type t = typeof(Action);
Console.WriteLine(t.IsClass);//true
(2)委託的聲明方式與一般的類不同,主要是爲了照顧可讀性和C/C++傳統
(3)注意聲明委託的位置,避免寫錯地方,結果聲明成嵌套類型
(4)委託與所封裝的方法必須“類型兼容”
delegate double Calc[double x,double y];
double Add[double x, double y]{return x + y;}
double Sub[double x, double y]{return x - y;}
double Mul[double x, double y]{return x * y;}
double Div[double x, double y]{return x / y;}
(1)返回值的數據類型一致
(2)參數列表在個數和數據類型上一致(參數名不需要一樣)
using System;
namespace Test
{
//自定義類
class Calculator
{
public double Add(double x, double y)
{
return x + y;
}
public double Sub(double x, double y)
{
return x - y;
}
public double Mul(double x, double y)
{
return x * y;
}
public double Div(double x, double y)
{
return x / y;
}
}
//自定義委託
public delegate double Calc(double x, double y);
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
//使用委託間接調用
Calc calc = new Calc(calculator.Add);
double x = 100.00;
double y = 200.00;
double z = 0.00;
//z = calc.Invoke(x, y);
//簡寫
z = calc(x, y);
Console.WriteLine(z);
Console.ReadKey();
}
}
}
委託的使用
將方法當作參數傳遞給另一個方法
(1)模板方法 借用指定的外部方法來產生結果
相當於填空題,常位於代碼中部,委託有返回值。
using System;
namespace Test
{
//產品
class Product
{
public string Name { get; set; }
}
//包裝
class Box
{
public Product Product { get; set; }
}
//包裝工廠
class WrapFactory
{
//使用Func委託 模板方法
public Box WrapProduct(Func<Product> getProduct)
{
Box box = new Box();
Product product = getProduct.Invoke();
box.Product = product;
return box;
}
}
//生產工廠
class ProductFactory
{
public Product Make()
{
Product product = new Product();
product.Name = "Pizza";
return product;
}
public Product Build()
{
Product product = new Product();
product.Name = "Car";
return product;
}
}
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
//使用模板方法
Func<Product> fn1 = new Func<Product>(productFactory.Make);
Func<Product> fn2 = new Func<Product>(productFactory.Build);
Box box1 = wrapFactory.WrapProduct(fn1);
Box box2 = wrapFactory.WrapProduct(fn2);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
Console.ReadKey();
}
}
}
(2)回調方法callback 調用指定的外部方法相當於流水線,常位於代碼末尾,委託無返回值。
(3)注意:委託難精通、易使用且功能強大,一旦被濫用後果非常嚴重。
- 委託造成方法級別的緊耦合,現實工作中要慎之又慎。
- 委託造成代碼可讀性下降,debug難度增加。
- 將委託回調、異步調用、多線程糾纏在一起,使得代碼難以閱讀和維護。
- 委託使用不當有可能造成內存泄漏和程序性能下降。