Delegate
-
-
- 1.什么是委托
- 2.委托概述
- 3.声明委托类型
- 4.创建委托对象
- 5.给委托赋值
- 6.组合委托
- 7.为委托添加/移除方法
- 8.调用委托
- 9.委托的示例
- 10.调用带返回值的委托
- 11.调用带引用参数的委托
- 12.匿名方法
- 12.1 使用匿名方法
- 12.2 匿名方法的语法
- 12.2.1 返回类型
- 12.2.2 参数
- 12.2.3 params参数
- 13.Lambda表达式
- 14.常见泛型委托
- 14.1 Action
- 14.2 Func
- 14.3 Predicate
-
1.什么是委托
可以认为委托是持有一个或多个方法的对象,此对象可以执行它所“持有”的方法。
delegate void Del(int value);
class Program
{
static int a = 1;
static void Up(int v)
{
Console.WriteLine($"{a + v}");
}
static void Down(int v)
{
Console.WriteLine($"{a - v}");
}
static void Main()
{
Del del1 = new Del(Up);
Del del2 = new Del(Down);
del1(1);
del2(1);
Console.ReadKey();
}
output
2
0
2.委托概述
使用委托与类的比较,入下图:
可以把委托看作是包含有序方法列表的对象,这些方法具有相同的签名和返回类型:
- 方法的列表称为调用列表
- 委托保存的方法可以来自任何类或结构,只有匹配一下两点:
- 委托的返回类型
- 委托的签名(包括ref和out修饰符)
- 调用列表中的方法可以是实例方法也可以是静态方法
- 在调用委托时,会执行其调用列表中的所有方法
3.声明委托类型
委托类型定义了委托对象调用列表中允许的方法的形式,如下示例代码声明了委托类型:
delegate void MyDel( int x ); // delegate为关键字,void为返回类型,MyDel为委托类型名,int为签名
委托声明与方法声明的不同:
- 以delegate关键字开头
- 没有方法主体
- 不需要在类内部声明,因为它是类型声明
4.创建委托对象
委托类型的变量声明如下:
MyDel delVar;
有两种创建委托对象的方式,第一种是使用new运算符, new运算符的操作数组成如下:
- 委托类型名
- 一组圆括号,其中包含作为调用列表中的第一个成员的方法的名字
delVar = new MyDel(Class1.Method1);
还可以使用以下快捷语法创建委托对象:
delVar = Class1.Method1;
5.给委托赋值
由于委托时引用类型,可以通过赋值来改变包含在委托变量中的引用,旧的委托对象会被垃圾回收器回收。
MyDel delVar;
delVar = Class1.Method1;
delVar = Class1.Method2; // Class1.Method1将会被GC
6.组合委托
委托可以调用额外的运算符来“组合”。
MyDel delA = Class1.Method1;
MyDel delB = Class1.Method2;
MyDel delC = delA + delB; // 组合调用列表
7.为委托添加/移除方法
使用+=运算符为委托添加方法:
MyDel delA = Class1.Method1; // 创建并初始化
delA = Class2.Method2; // 增加方法
delA = StaticClass.StaticMethod; // 增加方法
由于委托是不可变的,所以每添加一个方法其实是创建了一个新的委托。 使用-=运算符为委托移除方法:
delA -= Class2.Method2; // 从委托移除方法
移除一个方法同样是创建了一个新的委托。 移除委托时的注意事项:
- 如果再调用列表中有多个相同的方法,将从列表最后开始搜索,并移除第一个匹配方法
- 试图删除委托中不存在的方法没有效果
- 试图调用空委托会抛出异常,若调用列表为空,则委托是null
8.调用委托
可以向调用方法一样简单的调用委托。
Mydel delVar = inst.MyM1;
delVar += SCl.m3;
delVar += X.Act;
...
delVar(55);
在使用参数调用委托时会使用相同的参数值(如55)。
9.委托的示例
delegate void PrintFunction();
class Test
{
public void Print1()
{
Console.WriteLine("Print1 -- instance");
}
public static void Print2()
{
Console.WriteLine("Print2 -- static");
}
}
class Program
{
static void Main()
{
Test t = new Test();
PrintFunction pf = t.Print1;
pf += Test.Print2;
pf += t.Print1;
pf += Test.Print2;
if(null!=pf)
{
pf();
}
else
{
Console.WriteLine("Delegate is empty");
}
Console.ReadKey();
}
}
output
Print1 -- instance
Print2 -- static
Print1 -- instance
Print2 -- static
10.调用带返回值的委托
delegate int MyDel();
class MyClass
{
int value = 5;
public int Add2()
{
value += 2;
return value;
}
public int Add3()
{
value += 3;
return value;
}
}
class Program
{
static void Main()
{
MyClass mc = new MyClass();
MyDel md = mc.Add2;
md += mc.Add3;
md += mc.Add2;
Console.WriteLine($"value = {md()}");
Console.ReadKey();
}
}
output
value = 12
最后一个方法执行的返回值是委托的返回值。
11.调用带引用参数的委托
在调用委托列表中的下一个方法时,参数的新值回传给下一个方法
delegate void MyDel(ref int x);
class MyClass
{
public void Add2(ref int x) { x += 2; }
public void Add3(ref int x) { x += 3; }
static void Main()
{
MyClass mc = new MyClass();
MyDel md = mc.Add2;
md += mc.Add3;
md += mc.Add2;
int x = 5;
md(ref x);
Console.WriteLine($"value = {x}");
Console.ReadKey();
}
}
output
value = 12
12.匿名方法
匿名方法是在初始化委托时内联声明的方法。
12.1 使用匿名方法
- 声明委托变量时作为初始化表达式
- 组合委托时在赋值语句的右边
- 为委托增加事件时在赋值语句的右边
12.2 匿名方法的语法
匿名方法表达式的语法包含以下组成部分:
- delegate类型关键字
- 参数列表,无参可省略
- 语句块,包含匿名方法的代码
delegate ( Parameters ) { ImplementationCode }
12.2.1 返回类型
若委托的返回类型为int,则匿名方法中也应返回int。
delegate int Del(int n);
static void Main()
{
Del d = delegate(int x) { return x + 20; } // 返回一个整型值
}
12.2.2 参数
除了数组参数,匿名方法的参数列表必须与委托有以下匹配:
- 参数数量
- 参数类型及位置
- 修饰符 以下两个情况都满足可以简化匿名方法的参数列表:
- 委托的参数列表不包含任何out参数
- 匿名方法不使用任何参数
delegate void Del(int x);
Del d = delegate
{
PrintMessage();
CleanUp();
};
12.2.3 params参数
如果委托声明的参数列表包含params参数,那么匿名方法的参数列表将忽略params关键字:
delegate void Del(int x, params in[] y);
Del d = delegate(int x, int[] y){...};
delegate void MyDel(int x, params int[] y);
class MyClass
{
static void Main()
{
MyDel md = delegate (int x, int[] y)
{
foreach(var i in y)
{
Console.WriteLine(i);
}
};
md(1, 2, 3, 4);
Console.ReadKey();
}
}
output
2
3
4
13.Lambda表达式
使用Lambda表达式创建委托:
MyDel del = delegate(int x) {return x + 1;}; // 匿名方法
MyDel newdel = (int x) => {return x + 1;}; // Lambda表达式
Lambda表达式的简化规则:
- 编译器可从委托声明中得到委托参数的类型(ref和out参数须注明类型)
- 带有类型的参数列表称为显示类型
- 省略类型的参数列表称为隐式类型
- 如果只有一个隐式类型参数,则可以省略圆括号
- Lambda允许表达式主体是语句块或表达式 Lambda表达式的语法演示如下:
(参数,参数) (参数) 参数 () | => | {语句} 表达式 |
---|
Lambda表达式的语法由Lambda运算符和左边的参数部分以及右边的Lambda主体构成。
14.常见泛型委托
14.1 Action
Action<>委托可以拥有n个参数(0-16),无返回值。
class Program
{
static void Method1()
{
Console.WriteLine("without any parameter");
}
static void Method2(int i)
{
Console.WriteLine($"the int is {i}");
}
static void Method3(string s, double d)
{
Console.WriteLine($"the string is {s}, the double is {d}");
}
static void Main()
{
Action A1 = Method1;
Action<int> A2 = Method2;
Action<string, double> A3 = Method3;
A1();
A2(1);
A3("hello", 3.14);
Console.ReadKey();
}
}
output
without any parameter
the int is 1
the string is hello, the double is 3.14
14.2 Func
Func<>委托可以拥有n个参数(1-16),类型参数中的最后一个作为返回值类型。因此类型参数不能为空,至少有一个返回类型。
class Program
{
static int Method1(int i)
{
return i * 10;
}
static string Method2(int a, double d)
{
return (a + d).ToString();
}
static void Main()
{
Func<int, int> F1 = Method1;
Func<int, double, string> F2 = Method2;
Console.WriteLine($"{F1(10)}");
Console.WriteLine($"{F2(1, 3.14)}");
Console.ReadKey();
}
}
output
100
4.14
14.3 Predicate
Predicate<>委托拥有一个参数,其返回值为bool型。
class Program
{
static bool Method(int i)
{
return i > 0 ? true : false;
}
static void Main()
{
Predicate<int> P = Method;
Console.WriteLine(P(10));
Console.ReadKey();
}
}
output
True
通过匿名方法使用Predicate<>,
class Program
{
static void Main()
{
var anomynous = new Predicate<int>(delegate (int i) { return i > 0 ? true : false; });
Console.WriteLine(anomynous(1));
Console.ReadKey();
}
}
output
True