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.委托概述

使用委托与类的比较,入下图:
image
可以把委托看作是包含有序方法列表的对象,这些方法具有相同的签名和返回类型:

  • 方法的列表称为调用列表
  • 委托保存的方法可以来自任何类或结构,只有匹配一下两点:
    • 委托的返回类型
    • 委托的签名(包括ref和out修饰符)
  • 调用列表中的方法可以是实例方法也可以是静态方法
  • 在调用委托时,会执行其调用列表中的所有方法
    image

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; // 组合调用列表

image


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
posted @ 2019-04-03 15:28  Kyle0418  阅读(533)  评论(0编辑  收藏  举报