委托和匿名方法和Lambda表达式

半腔热情 提交于 2019-12-05 23:35:58

委托

  • 把一个函数作为参数传递
  • 函数可以直接赋值给一个委托对象、委托的签名必须跟函数的签名一样
  • 是C#中类型安全的,可以订阅一个或者多个具有相同签名方法的函数指针。

使用委托对的步骤

  • 1.声明一个委托类型。类似方法声明,但是没有实现块。
  • 2.使用该委托类型声明一个委托变量。
  • 3.创建委托类型的对象,并把它赋值给委托变量。新的委托对象包括指向某个方法的引用,这个方法和第一步定义的签名和返回类型一致。
  • 4.可以选择为委托方法增加其它方法。方法必须与定义的委托类型有相同的签名和返回类型。
  • 5.在代码中可以像调用方法一样调用委托。在调用委托时,其包含的每一个方法都会被执行。
    //声明委托对象
    delegate void MyDel(int value);
    class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();

            //声明委托变量
            MyDel del;

            //创建随机整数生成器对象,并得到0到99之间的一个随机数
            Random rand = new Random();
            int randomValue = rand.Next(99);

            //创建一个包含PrintLow 或者PrintHigh 的委托对象,并赋值给del变量
            del = randomValue < 50 
                ? new MyDel(program.PrintLow)
                : new MyDel(program.PrintHigh);
            del(randomValue);

            Console.ReadKey();
            //某次输出:38-Low Value  30-Low Value   92-High Value  

        }

        void PrintLow(int value)
        {
            Console.WriteLine("{0}-Low Value", value);
        }

        void PrintHigh(int value)
        {
            Console.WriteLine("{0}-High Value", value);
        }
    }

比较创建类与使用类和委托的过程

null 委托
声明类型 声明类 声明委托(类型)
声明类型的变量 声明类类型的变量 声明委托类型的变量
填充变量 创建类的实例并且把它的引用赋值给变量 创建委托的实例并且把它的引用赋值给变量,然后增加第一个方法
使用变量 使用类对象 调用委托对象
  • 委托保存的方法可以来自任何类或结构,只有委托的返回类型和签名匹配
  • 方法可以是静态方法,也可以是实例方法
  • 调用委托时,会执行其调用列表的所有方法

组合委托

MyDel delA = Method1;
MyDel delB = Method2;

//组合调用列表
MydDel delC = delA + delB;
  • 委托是恒定的,委托对象被创建后不能再被改变。

委托的添加方法和移除方法

  • 添加:+= 运算符
    • 实际是创建了一个新的委托
  • 移除:-= 运算符
    • 实际是创建了一个新的委托
    • 若在调用列表中的方法有多个实例,-=运算符将从列表最后开始搜索,并且移除第一个与方法匹配的实例。
    • 试图删除委托中不存在的方法没有效果
    • 试图调用空委托会抛出异常。

委托示例

    //定义一个没有返回值和参数的委托类型
    delegate void PrintFunction();
    class Program
    {
        static void Main(string[] args)
        {
            //创建一个测试类的实例
            Test t = new Test();

            //创建一个空委托
            PrintFunction pf;

            //实例化并初始化该委托
            pf = t.Print1;

            //给委托增加3个另外的方法
            pf += Test.Print2;
            pf += t.Print1;
            pf += Test.Print2;

            //现在委托有4个方法

            //确认委托有方法
            if(null!=pf)
            {
                //调用委托
                pf();
            }
            else
            {
                Console.WriteLine("Delegate is empty!");
            }

            Console.ReadKey();
            //输出:
            //Print1--instance
            //Print2--static
            //Print1--instance
            //Print2--static

            pf -= Test.Print2;
            if (null != pf)
            {
                //调用委托
                pf();
            }
            else
            {
                Console.WriteLine("Delegate is empty!");
            }
            Console.ReadKey();
            //输出:
            //Print1--instance
            //Print2--static
            //Print1--instance

            pf -= Test.Print2;
            pf -= t.Print1;
            pf -= Test.Print2;
            pf -= t.Print1;
            if (null != pf)
            {
                //调用委托
                pf();
            }
            else
            {
                Console.WriteLine("Delegate is empty!");
            }
            Console.ReadKey();
            //输出:Delegate is empty!
        }
    }

    class Test
    {
        public void Print1()
        {
            Console.WriteLine("Print1--instance");
        }
        public static void Print2()
        {
            Console.WriteLine("Print2--static");
        }
    }

调用带返回值的委托

  • 若委托有返回值并且在调用列表中有一个以上的方法,会发生以下的情况:
    • 调用列表中最后一个方法返回的值就是委托调用返回的值。
    • 调用列表中所有其它方法的返回值都会被忽略。
    //声明有返回值的方法
    delegate int MyDel();
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            //创建并初始化委托
            MyDel myDel = mc.Add2;
            //增加方法
            myDel += mc.Add3;
            myDel += mc.Add2;
            Console.WriteLine("Value:{0}", myDel());
            Console.ReadKey();
        }
        //输出:Value:12
    }

    class MyClass
    {
        int IntValue = 5;
        public int Add2()
        {
            IntValue += 2;
            return IntValue;
        }
        public int Add3()
        {
            IntValue += 3;
            return IntValue;
        }
    }

myDel();---> Add2();返回值7被忽略
--->Add3();返回值10被忽略--->Add2();使用返回值12

调用带引用参数的委托

  • 若委托有引用参数,参数值会根据调用列表中的一个或者多个方法的返回值而改变
    • 在调用委托列表中的下一个方法时,参数的新值(不是初始值)会传给下一个方法。
    delegate void MyDel(ref int x);
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            MyDel myDel = mc.Add2;
            myDel += mc.Add3;
            myDel += mc.Add2;
            int x = 5;
            myDel(ref x);
            Console.WriteLine("Value:{0}", x);
            Console.ReadKey();
            //输出:Value:12
        }
    }

    class MyClass
    {
        public void Add2(ref int x)
        {
            x += 2;
        }
        public void Add3(ref int x)
        {
            x += 3;
        }
    }

myDel();---> Add2(x=5);引用x的初始值
--->Add3(x=7);引用x新的输入值--->Add2(x=10);引用x新的输入值

匿名方法

  • 若方法只会被使用一次,除创建委托的语法需要,没有必要创建独立的具名方法。
  • 匿名方法是在初始化委托时内联声明的方法。
//命名方法
delegate int OtherDel(int InParam);
class Program
{
    public static void Add20(int x)
    {
        return x + 20;
    }
    static void Main()
    {
        OtherDel del = Add20;
        Console.WriteLine("{0}",del(5));
        Console.WriteLine("{0}",del(6));
        Console.ReadKey();
    }
    //输出:25 \n 26
}
//匿名方法
delegate int OtherDel(int InParam);
class Program
{
    static void Main()
    {
        OtherDel del = delegate(int x)
        {
            return x + 20;
        };
        Console.WriteLine("{0}",del(5));
        Console.WriteLine("{0}",del(6));
        Console.ReadKey();
    }
    //输出:25 \n 26
}

使用匿名方法

  • 1.声明委托变量时作为初始化表达式
  • 2.组合委托时在赋值语句的右边
  • 3.为委托增加事件时在赋值语句的右边

匿名方法的语法

delegate + (参数列表) + 语句块

返回类型

  • 匿名方法的返回类型和委托的返回类型一致
  • 例如:委托的返回类型是int ,则匿名方法的实现代码也必须在代码路径中返回int
//委托类型的返回类型为int
delegate int OtherDel(int InParam);
static void Main()
{
    OtherDel del = delegate(int x){
        return x + 20;//返回一个int
    };
}

参数

  • 除了参数数组,匿名方法的参数列表必须在==参数数量====参数类型及位置====修饰符==方面与委托匹配
  • 在满足==委托的参数列表不包含任何out参数==和==匿名方法不使用任何参数==的情况下,可以通过使用==圆括号为空==或==省略圆括号==来简化匿名方法的参数列表。
//声明委托类型
delegate void SomeDel(int x);
SomeDel SDel = delegate{//省略参数列表
    PrintMessage();
    Cleanup();
};

params参数

  • 若委托声明的参数列表包含了params参数,则匿名方法的参数列表将忽略params关键字
delegate void SomeDel(int x,params int[] y);
SomeDel SDel = delegate(int x,int[] y)
{
    ...
};

Lambda表达式

匿名方法转换为Lambda表达式的步骤

  • 1.删除delegate关键字
  • 2.在参数列表和匿名方法主体之间放Lamdba运算符=>。读作“goes to”
//匿名方法
MyDel del = delegate(int x)  {return x + 1;};
//Lambda表达式
MyDel del =         (int x)=>{return x + 1;};
  • 编译器通过推断可以进一步简化Lambda表达式
//1.省略类型参数
MyDel del =             (x)=>{return x+1;};
//2.只有一个隐式参数类型,省略圆括号
MyDel del =              x=>{return x+1;};
//3.若语句块包含了一个返回语句,则可将语句块替换为return后的表达式
MyDel del =              x=>        x+1;
  • 参数列表要点
    • 1.必须在参数数量、类型和位置上与委托相匹配
    • 2.表达式的参数列表中的参数不一定需要包含类型(隐式类型),除非委托有ref或者out参数,此时必须注明类型(显示类型)
    • 3.如果只有一个参数,并且是隐式类型的,周围的圆括号可以被省略,否则必须有括号。
    • 4.若没有参数,必须使用一组空的圆括号。
(参数,参数,) =>{语句}表达式
    (参数)    =>{语句}表达式
      参数      =>{语句}表达式
       ()      =>{语句}表达式
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!