using System;

namespace 委托
{
class Program
{
//委托类型:定义了委托实例可以调用哪类方法,
//具体来说,委托类型定义了方法的返回值类型了参数
//delegate 要调用方法的返回值类型 委托类型名 (参数)

    //例:定义一个带参数,无返回值的委托
    //(创建的委托必须与索要传递的方法,返回值/参数类型相同)
    public delegate void SayHelloDlg(string content);

    //SayHelloDlg类型的事件,声明事件,public event 委托类型 事件名
    public static event SayHelloDlg SayHelloEvent;


    //主函数
    static void Main(string[] args)
    {
        //委托的声明
        //委托类型名 = 目标方法
        SayHelloDlg dlg = SayHello;
        //委托的使用方法
        //dlg.Invoke("老王");
        dlg("老王");            
        //委托的解释:将方法以变量形式传递,并以方法形式执行。(以委托来调用方法,解耦)
        //SayHelloDlg dlg = new SayHelloDlg(SayHello);

        //(多播委托)委托链
        dlg += SayBay;//添加
        dlg("老八");  //(实际+,-,+=,-+编译成 System.Delegate的Combine和Remove两个静态方法)
        dlg -= SayBay;//注销
        dlg("老八");
        //如果调用的方法有返回值,那么接收的返回值是调用的最后一个方法的返回值,前面调用的方法返回值就被舍弃了。


        //匿名函数
        SayHelloDlg dlg1 = delegate (string name)
        {
            Console.WriteLine("{0},是匿名函数", name);
        };

        dlg1("老七");


        //lamda语句
        SayHelloDlg dlg2 = (name) =>
         {
             Console.WriteLine("{0},是lamda语句", name);
         };

        dlg2("老四");

        //事件
        **事件不能在类的外部进行调用(委托可以),但可在类的外部注册。因此事件更安全**
        **事件委托区别**
        **事件使用与委托基本一致,事件是对委托的封装,使用更安全**
        **主要区别**
        **1.事件不能在外部使用赋值负号"=",只能进行+,-,进行事添加与注销,委托那里都能使用**
        **2.事件不能在外部执行,委托在那里都能执行**
        **3.事件 不能作为函数中的临时变量,只能在类/结构体中的成员变量,委托可以作为临时变量**
        //注册事件
       // SayHelloEvent += new SayHelloDlg(Program_SayHelloEvent);//实际事件上挂的是委托链对象
        SayHelloEvent += Program_SayHelloEvent;//上行代码简写
        //SayHelloEvent -= Program_SayHelloEvent; 

        if (SayHelloEvent != null)
        {
            SayHelloEvent("小八");//调用事件
        }
    }

    private static void Program_SayHelloEvent(string content)
    {
        Console.WriteLine("{0},是事件", content);
    }



    public static void SayHello(string name)
    {
        Console.WriteLine("{0},你好啊", name);
    }

    //表达式形式
    //public static void SayHello(string name) => Console.WriteLine("{0},你好啊", name);


    public static void SayBay(string name)
    {
        Console.WriteLine("{0},再见啊",name);
    }
}

}






//Part2-----------------------------------------------------------------------
泛型委托,
Action,无返回值
using System;

namespace System委托
{
class Program
{
static void Main(string[] args)
{
Action a = PrintString;//系统预定义的无返回值的委托类型,可指向一个无参数,返回值的方法

        Action<int> b = PrintInt;//泛型Action定义了一个委托,可指向有一个int类型参数,无返回值方法
        PrintInt(1);

        Action<string> c = PrintString;//泛型Action定义了一个委托,可指向有一个string类型参数,无返回值方法,
        //(存在重名方法时系统会自动匹配)合适的方法
        PrintString("Get");

        Action<int, int> d = PrintInt;//最多可附带16个参数,
        PrintInt(2, 3);
    }

    static void PrintString()
    {
        Console.WriteLine("Action委托");
    }
    static void PrintString(string a)
    {
        Console.WriteLine("Action委托" + a);
    }
    static void PrintInt(int a)
    {
        Console.WriteLine(a);
    }
    static void PrintInt(int a, int b)
    {
        Console.WriteLine(a + b);
    }
}

}

Func,有返回值(返回值为最后一个泛型的参数)
static void Main(string[] args)
{
Func a = Fun1;//系统预定义的有返回值的委托类型,
Console.WriteLine(a());

        Func<int, int, string> b = Fun2;//多个泛型时,最后一个泛型为返回值类型,
        //前面的泛型与参数类型一一对应。
        Console.WriteLine(b(2, 3));
    }

    static int Fun1()
    {
        return 1;
    }
    static string Fun2(int a,int b)
    {
        return (a + b).ToString();
    }

    多播委托-----又返回值时只返回最后一个方法的返回值,中间方法出现异常时,后面的方法将不会被调用
            static void Text1()
    {
        Console.WriteLine("Text1");
    }
    static void Text2()
    {
        Console.WriteLine("Text2");
    }
    static void Main(string[] args)
    {
        Action a = Text1;
        a += Text2;//多播委托(委托链),可指向多个与委托同类型的方法
        a();
        a -=Text2;//注销一个委托链上的方法,当一个委托没有指向任何方法时注销会异常
        if (a != null)
            a();
        //多播委托方法
        Delegate[] delegates = a.GetInvocationList();//返回该委托中所有方法的单独委托(解耦)
        foreach (var /*Action*/ d in delegates)
        {
            d.DynamicInvoke();//获取委托后单独执行//有参数时可填入括号
        }
    }

//匿名方法
static int Text1(int a,int b)
{
return a + b;
}

    static void Main(string[] args)
    {
        //Func<int, int, int> a = Text1;
        Func<int, int, int> a = delegate (int a, int b)//delegate定义 匿名方法(只使用一次时,通常用于回调函数(动作完成后执行))
          {
              return a + b;
          };
          //Lambda表达式--用来代替匿名方法
          Func<int, int, int> a = (a, b) =>//不需在指定类型(委托中以指定类型)
          {
              return a + b;
          };
        Console.WriteLine(a(2, 3));
        Func<int, int> text2 = a => a+= 1;//lambda表达式只有一个参数时参数可不加括号,当函数体只有一句时,也可不写大括号,
        //需要也可不加上return语句,可自动取值。
        /*Func<int, int> text3 = (a) =>//text2完整形式
         {
             return a += 1;
         };*/
        Console.WriteLine(text2(2));

        //Lambda表达式可访问外部变量(应用不安全,返回值不只有参数决定谨慎使用)
        int someValue = 5;
        Func<int, int> outValue = x => x += someValue;
        Console.WriteLine(outValue(5));
        someValue = 15;
        Console.WriteLine(outValue(5));
    }