Delegate、Event、Lambda

Delegate、Event、Lambda

  • 委托的本质

委托是用于委托函数,其本质就是函数指针,即一个指向函数的指针,通过这个指针可以调用这个函数。在C/C++中,我们可以除了可以使用指针实现对数据的引用,同样还可以使用函数指针实现对函数的引用。由于这个机制比较复杂,而且非常容易出错,所以在C#早期的版本并没有实现这个功能。直到C#3.0,委托的追加,才实现了这个功能。

  • 委托的目的

对于大部分程序员,我们已经习惯了对函数的调用和对数据的传送,即数据是可以传送并且也经常用于传送,在函数的参数列表中,都是使用数据类型来定义数据。大部分场景中,我们并不需要对函数进行传递。

这种传统有一个弊端:我们可以实现对数据的需求分离,但是对函数仍然是依赖的。根据我们基本的设计模式来说,高内聚和低耦合是我们追求的目标。虽然通过抽象(如接口)能够在一定程度上解决这个问题,但是抽象本身也是有名称的,对名称仍然是有依赖的。

为了解决这个问题,委托就起了作用。我们只需要知道一个操作的参数列表和返回类型即可进行操作,连函数名称都不需要了。所以通过委托机制,我们彻底摆脱了对函数名称的依赖。即,只需要知道所有的输入和输出的类型即可。甚至于在一些场景中,输入和输出都用object类型,我们连数据类型也不需要,只需要知道输入和输出的个数即可。再进一步,我们如果把输入和输出都定义成object[]类型,再加上委托机制,可以实现最一般、最通用的函数,实现对所有函数的调用。
————————————————————————————————————————————————

Delegate

0、应用场景

委托就是函数指针,委托类型是微软口中的安全性给函数指针加的限制,函数在内存中占用了一块内存,外部调用该函数的时候除了实例化类,再通过了类调用以外,可以定义一个和函数同类型的委托,再实例化一个委托,并在实例化的过程中将同类型函数的内存地址打包,然后传给外界,外界就能直接调用该函数,在回调的时候比较好用。比如有多个类里的函数,调用另一个类里的执行函数,并在结束后回调调用函数所在类的某一个函数,因为存在多个类调用的可能,不可能实例化全部可能调用的类,那太傻了,这个时候委托就发挥作用了, 委托可以作为实参传递,因为它本质上就是一个储存了函数所在内存地址的变量。
简介

在C#中,最基本的内容不外乎数据和函数。
就象做菜一样,数据是原料,函数是操作。
C#中提供了多种数据类型用于表示数据,同时提供了函数,用于定义对数据的操作。
举例来说,以下函数定义了一个基本操作函数将传入字符串以指定格式输出。

public void Show(string message)
{
	Console.WriteLine("[{0}] {1}", DateTime.Now.ToString(), message);
}

Show函数接收到传入数据,然后调用Console.WriteLine进行显示。因为在这个大家以为常的过程中,我们传输的都是数据。
既然数据和操作是不可分割的,那么我们就有了这样的一个问题:除了数据,我们是否也可以传递函数?

答案显然是肯定的。除了数据,在C#中也可以传递函数。
为了表示函数,C#引用了一个新的概念就是委托。
用最通俗的话来说,委托就是对函数的封装,用于代表某个函数,从而便于在函数传参中进行调用。

1、 委托定义

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

除了回调意外,委托的另一个用处就是和事件绑定,事件和回调差不多,但事件可以做到一对多,比如说:
一个班级 = 命名空间,
班级里的学生 = 类 ,
老师 = 事件发布者,
上课= 事件,
起立=事件发生时学生类应该执行的函数,
一个一个调用还是太傻了,事件的优势就在这里,只需要判断事件是否发生,如果发生,就告诉所有订阅者执行绑定在事件上的函数。

// This delegate can point to any method,
    // taking two integers and returning an integer.
    //该委托可以指向任何方法,
    //取两个整数并返回一个整数。
    public delegate int BinaryOp( int x, int y );
   #region SimpleMath class
    // This class contains methods BinaryOp will
    // point to.
    public class SimpleMath
    {
        public int Add( int x, int y )
        { return x + y; }
        public int Subtract( int x, int y )
        { return x - y; }
        public static int SquareNumber( int a )
        { return a * a; }
    } 
    #endregion

 class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine("***** Simple Delegate Example *****\n");

            // Create a BinaryOp delegate object that
            // "points to" SimpleMath.Add().           
           
            //创建BinaryOp委托对象
            //指向“SimpleMath.Add()”。
            SimpleMath m = new SimpleMath();

            BinaryOp b = new BinaryOp(m.Add);
            DisplayDelegateInfo(b);

            // Invoke Add() method indirectly using delegate object.
            Console.WriteLine("10 + 10 is {0}", b(10, 10));

            // Compiler error! Method does not match delegate pattern!
            // BinaryOp b2 = new BinaryOp(SimpleMath.SquareNumber);

            Console.ReadLine();
        }

        #region Display delegate info
        static void DisplayDelegateInfo( Delegate delObj )
        {
            // Print the names of each member in the
            // delegate's invocation list.
            //打印代理调用列表中每个成员的名称。
            foreach (Delegate d in delObj.GetInvocationList())
            {
                Console.WriteLine("Method Name: {0}", d.Method);
                Console.WriteLine("Type Name: {0}", d.Target);
            }
        } 
        #endregion

    }

在这里插入图片描述

2、委托原理解析

在这里插入图片描述
在这里插入图片描述

3、组合(多播)委托

在这里插入图片描述

4、委托委托的高级试用(同步和异步调用)

在这里插入图片描述
第一种:直接同步调用

在这里插入图片描述
第二种:间接同步调用
在这里插入图片描述

第三种:多播委托同步调用
在这里插入图片描述
第一种:直接异步调用
在这里插入图片描述
第二种试用线程显式异步调用
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5、委托存在的问题–引入事件

1.委托定义的步骤比较麻烦(重复代码)(定义委托,声明必要的成员变量以及创建自定义的注册、注销方法来保护封装等)。
2.应用程序回调机制存在问题。如果我们没有把委托成员的变量定义为私有的,调用者可以直接访问委托对象。这样调用者就可以直接把变量重新赋值为新的委托对象,及时删除委托方法列表。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PublicDelegateProblem
{
    public class Car
    {
        public delegate void CarEngineHandler( string msgForCaller );

        // Now a public member!现在是一名公众成员
        public CarEngineHandler listOfHandlers;

        // Just fire out the Exploded notification. 只需发出爆炸通知。
        public void Accelerate( int delta )
        {
            if (listOfHandlers != null)
                listOfHandlers("Sorry, this car is dead...");
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PublicDelegateProblem
{
    class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine("***** Agh!  No Encapsulation! *****\n");
            // Make a Car.
            Car myCar = new Car();
            // We have direct access to the delegate! 我们可以直接访问委托!
            myCar.listOfHandlers = new Car.CarEngineHandler(CallWhenExploded);
            myCar.Accelerate(10);

            // We can now assign to a whole new object...我们现在可以指定给一个全新的对象。。。
            // confusing at best.充其量令人困惑。
            myCar.listOfHandlers = new Car.CarEngineHandler(CallHereToo);
            myCar.Accelerate(10);

            // The caller can also directly invoke the delegate!调用方也可以直接调用委托!
            myCar.listOfHandlers.Invoke("hee, hee, hee...");
            Console.ReadLine();
        }

        static void CallWhenExploded( string msg )
        { Console.WriteLine(msg); }

        static void CallHereToo( string msg )
        { Console.WriteLine(msg); }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CarDelegateMethodGroupConversion
{
    public class Car
    {
        // Internal state data.
        public int CurrentSpeed { get; set; }
        public int MaxSpeed { get; set; }
        public string PetName { get; set; }

        // Is the car alive or dead?
        private bool carIsDead;

        // Class constructors.
        public Car() { MaxSpeed = 100; }
        public Car( string name, int maxSp, int currSp )
        {
            CurrentSpeed = currSp;
            MaxSpeed = maxSp;
            PetName = name;
        }

        #region Delegate infrastructure
        // 1) Define a delegate type.定义委托类型。
        public delegate void CarEngineHandler( string msgForCaller );

        // 2) Define a member variable of this delegate.定义此委托的成员变量。
        private CarEngineHandler listOfHandlers;

        // 3) Add registration function for the caller.为呼叫者添加注册功能。
        public void RegisterWithCarEngine( CarEngineHandler methodToCall )
        {
            // listOfHandlers = methodToCall;
            // listOfHandlers += methodToCall; 
            // listOfHandlers += methodToCall; 
            if (listOfHandlers == null)
                listOfHandlers = methodToCall;
            else
                Delegate.Combine(listOfHandlers, methodToCall);
        }

        public void UnRegisterWithCarEngine( CarEngineHandler methodToCall )
        {
            listOfHandlers -= methodToCall;
        }

        // 4) Implement the Accelerate() method to invoke the delegate’s 
        //    invocation list under the correct circumstances.
        public void Accelerate( int delta )
        {
            // If this car is 'dead', send dead message.
            if (carIsDead)
            {
                if (listOfHandlers != null)
                    listOfHandlers("Sorry, this car is dead...");
            }
            else
            {
                CurrentSpeed += delta;

                // Is this car 'almost dead'?
                if (10 == (MaxSpeed - CurrentSpeed)
                    && listOfHandlers != null)
                {
                    listOfHandlers("Careful buddy!  Gonna blow!");
                }

                if (CurrentSpeed >= MaxSpeed)
                    carIsDead = true;
                else
                    Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
            }
        }
        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CarDelegateMethodGroupConversion
{
    class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine("***** Method Group Conversion *****\n");
            Car c1 = new Car();

            // Register the simple method name.
            c1.RegisterWithCarEngine(CallMeHere);

            Console.WriteLine("***** Speeding up *****");
            for (int i = 0; i < 6; i++)
                c1.Accelerate(20);

            // Unregister the simple method name.
            c1.UnRegisterWithCarEngine(CallMeHere);

            // No more notifications!
            for (int i = 0; i < 6; i++)
                c1.Accelerate(20);

            Console.ReadLine();
        }

        static void CallMeHere( string msg )
        {
            Console.WriteLine("=> Message from Car: {0}", msg);
        }
    }
}

Event

1、事件的定义

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2、事件要完成的逻辑关系和原理

2.1、发布与订阅的概念

在这里插入图片描述
在这里插入图片描述

2.2、发布者与订阅者解析

在这里插入图片描述
在这里插入图片描述

2.2、发布者与订阅者代码实现逻辑

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3、自定义事件参数

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PrimAndProperCarEvents
{
    public class CarEventArgs : EventArgs
    {
        public readonly string msg;
        public CarEventArgs(string message)
        {
            msg = message;
        }
    }

}

 // This delegate works in conjunction with the
        // Car's events.
        //该代表与汽车活动一起工作。
        public delegate void CarEngineHandler(object sender, CarEventArgs e);

        // This car can send these events.这辆车可以发送这些事件。
        public event CarEngineHandler Exploded;
        public event CarEngineHandler AboutToBlow;
using System;
using System.Collections.Generic;
using System.Text;

namespace Demo.BytesIO.ChatSdk.Entitiy
{
    /// <summary>
    /// 文件发送中的事件参数
    /// </summary>
    public class FileSendingEventArgs : EventArgs
    {
        public string FileName { get; set; }
        public int SentLen { get; set; }
        public long FileSize { get; set; }
    }

    /// <summary>
    /// 文件已发送的事件参数
    /// </summary>
    public class FileSentEventArgs : EventArgs
    {
        public string FileName { get; internal set; }
    }

    /// <summary>
    /// 接收到文本消息的事件参数
    /// </summary>
    public class TextReceivedEventArgs : EventArgs
    {
        public string Text { get; internal set; }
    }

    /// <summary>
    /// 接收到抖动消息的事件参数
    /// </summary>
    public class ShakeReceivedEventArgs : EventArgs
    {
    }

    /// <summary>
    /// 开始接收文件的事件参数
    /// </summary>
    public class FileAcceptEventArgs : EventArgs
    {
        public string FilePath { get; internal set; }
    }

    /// <summary>
    /// 文件接收完成的事件参数
    /// </summary>
    public class FileReceivedEventArgs : EventArgs
    {
        public string FilePath { get; internal set; }
    }



}

using Demo.BytesIO.ChatProtocol;
using Demo.BytesIO.ChatSdk.Entitiy;
using STTech.BytesIO.Core;
using STTech.BytesIO.Core.Component;
using STTech.BytesIO.Tcp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Demo.BytesIO.ChatSdk
{
    public class ChatClient : TcpClient, IUnpackerSupport<ChatMessageResponse>
    {
        public Unpacker<ChatMessageResponse> Unpacker { get; }

        private Dictionary<string, FileStream> dictFileStream = new Dictionary<string, FileStream>();

        public string FileSavePath { get; set; } = "./Recv";

        /// <summary>
        /// 文件发送中的事件
        /// </summary>
        public event EventHandler<FileSendingEventArgs> FileSending ;

        /// <summary>
        /// 文件已发送的事件
        /// </summary>
        public event EventHandler<FileSentEventArgs> FileSent;

        /// <summary>
        /// 接收到文本消息的事件
        /// </summary>
        public event EventHandler<TextReceivedEventArgs> TextReceived;

        /// <summary>
        /// 接收到抖动消息的事件
        /// </summary>
        public event EventHandler<ShakeReceivedEventArgs> ShakeReceived;

        /// <summary>
        /// 开始接收文件的事件
        /// </summary>
        public event EventHandler<FileAcceptEventArgs> FileAccept;

        /// <summary>
        /// 文件接收完成的事件
        /// </summary>
        public event EventHandler<FileReceivedEventArgs> FileReceived;



        public ChatClient()
        {
            Unpacker = new Unpacker<ChatMessageResponse>(this,bytes => {
                var len = bytes.Count();
                if (len < 5)
                {
                    return 0;
                }
                var arr = bytes.Take(5).ToArray();
                var dataLen = BitConverter.ToUInt16(arr,1);
                var argsLen = BitConverter.ToUInt16(arr,3);
                return 5 + dataLen + argsLen;
            });
            this.BindUnpacker(Unpacker);
            Unpacker.OnDataParsed += Unpacker_OnDataParsed;

            this.OnDataReceived += ChatClient_OnDataReceived;
        }

        private void ChatClient_OnDataReceived(object sender, STTech.BytesIO.Core.Entity.DataReceivedEventArgs e)
        {
            Unpacker.Input(e.Data);
        }

        private void Unpacker_OnDataParsed(object sender, DataParsedEventArgs<ChatMessageResponse> e)
        {
            var resp = e.Data;
            switch (resp.Type)
            {
                case ChatMessageType.Text:
                    // Print($"收到消息:{resp.Data.EncodeToString()}");
                    TextReceived?.Invoke(this,new TextReceivedEventArgs() { Text = resp.Data.EncodeToString() });
                    break;
                case ChatMessageType.FileInfo:
                case ChatMessageType.FileContent:
                case ChatMessageType.FileEnd:
                    var fileName = resp.Args.EncodeToString();
                    var filePath = Path.Combine(FileSavePath, fileName);

                    if (resp.Type == ChatMessageType.FileInfo)
                    {
                        if (File.Exists(filePath))
                        {
                            File.Delete(filePath);
                        }
                        Directory.CreateDirectory(FileSavePath);
                        // Print($"正在接收文件:{fileName}");

                        FileAccept?.Invoke(this,new FileAcceptEventArgs() {
                            FilePath = filePath
                        });

                        dictFileStream[filePath] = new FileStream(filePath, FileMode.Append, FileAccess.Write);

                    }
                    else if (resp.Type == ChatMessageType.FileContent)
                    {
                        FileStream fileStream = dictFileStream[filePath];
                        lock (fileStream)
                        {
                            fileStream.Write(resp.Data, 0, resp.Data.Length);
                        }
                    }
                    else if (resp.Type == ChatMessageType.FileEnd)
                    {
                        FileStream fileStream = dictFileStream[filePath];
                        fileStream.Close();
                        fileStream.Dispose();

                        //Process.Start("Explorer.exe", $"/select,{filePath}");
                        //Print($"完成接收文件:{filePath}");
                        FileReceived?.Invoke(this, new FileReceivedEventArgs()
                        {
                            FilePath = filePath
                        });
                    }
                    break;
                case ChatMessageType.Shake:
                    // this.ParentForm.Shake();
                    // Print("收到一个窗口抖动");
                    ShakeReceived?.Invoke(this,new ShakeReceivedEventArgs());
                    break;

            }
        }

        public void SendText(string text)
        {
            this.Send(new ChatMessageRequest()
            {
                Type = ChatMessageType.Text,
                Data = text.GetBytes(),
            });
        }

        public void SendShake()
        {
            this.Send(new ChatMessageRequest()
            {
                Type = ChatMessageType.Shake,
            });
        }

        public void SendFile(string filePath)
        {
            var fileName = Path.GetFileName(filePath);
            var fileSize = new FileInfo(filePath).Length;

            ChatMessageRequest req1 = new ChatMessageRequest()
            {
                Type = ChatMessageType.FileInfo,
                Args = fileName.GetBytes(),
            };
            this.SendAsync(req1);
            // Print($"准备发送文件: {fileName}, 总大小: {fileSize}字节");

            int sentCount = 0;
            using (FileStream fs = File.OpenRead(filePath))
            {
                byte[] buffer = new byte[10000];
                int len = 0;
                while ((len = fs.Read(buffer, 0, buffer.Length)) > 0)
                {
                    Thread.Sleep(50);

                    ChatMessageRequest req2 = new ChatMessageRequest()
                    {
                        Type = ChatMessageType.FileContent,
                        Data = len == buffer.Length ? buffer : buffer.Take(len).ToArray(),
                        Args = fileName.GetBytes(),
                    };
                    this.SendAsync(req2);

                    sentCount += len;

                    // Print($"发送文件:{fileName} ({sentCount * 100.0 / fileSize}%)");
                    FileSending?.Invoke(this,new FileSendingEventArgs() { 
                        FileName = fileName,
                        SentLen = sentCount,
                        FileSize = fileSize,
                    });
                }
            }

            Thread.Sleep(50);

            ChatMessageRequest req3 = new ChatMessageRequest()
            {
                Type = ChatMessageType.FileEnd,
                Args = fileName.GetBytes(),
            };
            this.SendAsync(req3);

            // Print($"文件发送完毕:{fileName}");
            FileSent?.Invoke(this,new FileSentEventArgs() {
                FileName = fileName,
            });
        }
    }
}

4、事件和委托关系

在这里插入图片描述

5、匿名方法

在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AnonymousMethods
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** Anonymous Methods *****\n");
            Car c1 = new Car("SlugBug", 100, 10);

            int aboutToBlowCounter = 0;

            // Register event handlers as anonymous methods.将事件处理程序注册为匿名方法。
            c1.AboutToBlow += delegate
            {
                aboutToBlowCounter++;
                Console.WriteLine("Eek! Going too fast!");
            };

            c1.AboutToBlow += delegate(object sender, CarEventArgs e)
            {
                aboutToBlowCounter++;
                Console.WriteLine("Message from Car: {0}", e.msg);
            };

            c1.Exploded += delegate(object sender, CarEventArgs e)
            {
                Console.WriteLine("Fatal Message from Car: {0}", e.msg);
            };

            // This will eventually trigger the events.
            for (int i = 0; i < 6; i++)
                c1.Accelerate(20);

            Console.WriteLine("AboutToBlow event was fired {0} times.", aboutToBlowCounter);
            Console.ReadLine();
        }
    }
}

Lambda ==>

1. 概述

C# 支持内敛处理事件,通过直接把一段代码语句复制给事件(使用匿名方法),而不是构建被底委托调用的独立方法。
Lambda 表达式只是用更简单的方式来写匿名方法,彻底简化了对.net 委托类型的使用。

2. Predicate 类介绍

Predicate 英[ˈpredɪkət, ˈpredɪkeɪt] 美[ˈpredɪkət, ˈpredɪkeɪt]谓语部分;谓语;谓词;断言

Predicate类代表的函数是用于返回 bool 型的函数。
由于在系统中有大量的判断,所以bool型的使用非常普遍。因此,微软专门定义了这个类。
主要要求如下:

  • 返回类型必需为bool,即逻辑判断型。
  • 参数列表类型不限制,长度最多为16。
  • 以下是Predicate类 的定义原型。
public delegate bool Predicate<T1>(T1 t1);
public delegate bool Predicate<T1, T2>(T1 t1, T2 t2);
...
public delegate bool Predicate<in T1,in T2,in T3,in T4,in T5,in T6,in T7,in T8,in T9,in T10,in T11,in T12,in T13,in T14,in T15,in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

3. Action 类介绍

Action 是.NET类库从3.0开始新增的内置委托,用于实现对无返回函数的调用。以下示例定义了一个Action,在使用已经有函数Show赋值后,再进行调用。

public override void Test()
{
	Action<string> fun = Show; 
	fun("hello");
}

public void Show(string s)
{
	Console.WriteLine(s);
}

既然Action类是用于表示函数的,那么函数的参数列表和返回类型就很重要,关于这两点Action类规定如下:

返回类型必需为void,即不返回任何值。这也是为什么这类函数被称之为Action(即运作)的原因了。
参数列表类型不限制,长度最多为16。补充一句,这个数据是微软在调查了Windows底层代码得出的来的,即16个足够满足所有需求(如果极端情况不够用,那么就是设计问题,因为部分参数一定可以合并为一个类,然后再进行调用)。以下是1-16个参数的Action定义。从定义来看,本质上也就是使用delegate关键字定义的普通委托。
————————————————

public delegate void Action<T1>(T1 t1);
public delegate void Action<T1, T2>(T1 t1, T2 t2);
...
public delegate void Action<in T1,in T2,in T3,in T4,in T5,in T6,in T7,in T8,in T9,in T10,in T11,in T12,in T13,in T14,in T15,in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);

委托的初始化
委托的初始化有以下三种方式:

  1. 直接使用函数赋值
  2. 使用匿名函数定义
  3. 使用Lambda表达式
    如下所示。
public override void Test()
{
	// 1.通过函数赋值初始化
	Action<string> act1 = Show;
	// 2.通过匿名函数定义
	Action<string> act2 = delegate(string s)
	{
		Console.WriteLine(s);
	}
	// 3.通过lambda
	Action<string> act3 = s => Console.WriteLine(s);
}

public void Show(string s)
{
	Console.WriteLine(s);
}

4. Func 类介绍

Func是.NET类库从3.0开始新增的内置委托,用于实现返回类型为任何有返回类型的函数的调用。Func的参数列表的格式为传入类型参数列表+返回列表。举例来说,以下示例定义了一个Fun, IsAdult(int) 赋值后,再进行调用。

public override void Test()
{
	// Predicate示例
	Predicate<int> pred = Show; 
	if(!pred(20))
	{
		Console.WriteLine("未成年");
	}
	
	// Func示例
	Func<int, bool> func = Show;  
	if(!pred(20))
	{
		Console.WriteLine("未成年");
	}
	 
}

public void IsAdult(int age)
{
	return age > 18;
}

如上所示,使用Func可以代替Predicate类。唯一的区别在于,Func的参数列表中,最后多了一个表示返回类型。如func有两个参数,第一个表示传入参数为int型,第二个表示返回类型为bool型。
Func类
Func类可以表示任意有返回类型型的函数。

  • 返回类型必需为bool,即逻辑判断型。
  • 参数列表类型不限制,长度最多为16。
  • 以下是Predicate类 的定义原型。
public delegate void Func<T1>(T1 t1, Tr tr);
public delegate void Func<T1, T2>(T1 t1, T2 t2, Tr tr);
...
public delegate void Func<in T1,in T2,in T3,in T4,in T5,in T6,in T7,in T8,in T9,in T10,in T11,in T12,in T13,in T14,in T15,in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, Tr tr);

List<string> names = new List<string>();
names.Add("Tom");
names.Add("Jack");
names.Add("Lucy");
Func<string, bool> predicate = new Func<string, bool>
(
	(name) =>
	{
		return true;
	}
);
Console.WriteLine("长度为3的名子个数为:" + names.Count(predicate));
Console.WriteLine("长度为3的名子第1个是:" + names.First(predicate));

// 定义
Func<string, bool> predicate = (name) => name.Length == 3;
// 或直接使用
Console.WriteLine("长度为3的名子个数为:" + names.Count(name => name.Length == 3));

通过委托机制,将函数调用的抽象又提高至一个层面,把函数的定义关注于输入和输出的数据类型。通过把操作的定义,实现进高一个层次的定义和调用。

5. 案例

但你会发现:

(1)List类的所有方法如Exists, Find, FindAll, FindIndex等全是接受Predicate类型参数的。
(2)而List类实现的接口IEnumerable的所有扩展方法如All,Any,First,FirstOrDefault,Where等都是接受Func<TSource, bool>类型的参数的。
同样是List中的方法,为什么会使用不同的委托类型呢?
先看一下定义:
Func<T, bool> :表示有传入T类型的参数,返回值为bool的委托
Predicate:表示有传入T类型的参数,返回值为bool的委托
在定义上不存在任何差别。
查看Predicate和Func的定义如下:

public delegate bool Predicate<in T>(T obj);
public delegate TResult Func<in T, out TResult>(T arg);

当Func定义中TResult固定为bool,则两个定义应该是完全一致,List中的方法参数本应该统一才对。
结论:

Func<T, bool>是对delegate bool Predicate<T>(T obj)的简化,
Predicate<T>又是对Func<T, bool>的简化,

其实,就是一个东西。
List中的方法应该统一参数。

附上以下内容:

Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。这个是祖宗。
Func可以接受0个至16个传入参数,必须具有返回值。
Action可以接受0个至16个传入参数,无返回值。
Predicate只能接受一个传入参数,返回值为bool类型。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleLambdaExpressions
{
    class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine("***** Fun with Lambdas *****\n");
            TraditionalDelegateSyntax();
            AnonymousMethodSyntax();
            Console.WriteLine();
            LambdaExpressionSyntax();
            Console.ReadLine();
        }

        #region Traditional delegate syntax
        static void TraditionalDelegateSyntax()
        {
            // Make a list of integers.
            List<int> list = new List<int>();
            list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

            // Call FindAll() using traditional delegate syntax.
            Predicate<int> callback = new Predicate<int>(IsEvenNumber);
            List<int> evenNumbers = list.FindAll(callback);

            Console.WriteLine("Here are your even numbers:");
            foreach (int evenNumber in evenNumbers)
            {
                Console.Write("{0}\t", evenNumber);
            }
            Console.WriteLine();
        }

        // Target for the Predicate<> delegate.
        //Predicate  英[ˈpredɪkət, ˈpredɪkeɪt] 美[ˈpredɪkət, ˈpredɪkeɪt]谓语部分;谓语;谓词;断言
        static bool IsEvenNumber( int i )
        {
            // Is it an even number?
            return (i % 2) == 0;
        }
        #endregion

        #region Anonymous method systax
        static void AnonymousMethodSyntax()
        {
            // Make a list of integers.
            List<int> list = new List<int>();
            list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

            // Now, use an anonymous method.
            List<int> evenNumbers = list.FindAll(delegate( int i )
            { return (i % 2) == 0; });

            Console.WriteLine("Here are your even numbers:");
            foreach (int evenNumber in evenNumbers)
            {
                Console.Write("{0}\t", evenNumber);
            }
            Console.WriteLine();
        } 
        #endregion

        #region Lambda!
        static void LambdaExpressionSyntax()
        {
            // Make a list of integers.
            List<int> list = new List<int>();
            list.AddRange(new int[] { 20, 1, 4, 8, 9, 44 });

            // Now process each argument within a group of
            // code statements.
            List<int> evenNumbers = list.FindAll(( i ) =>
            {
                Console.WriteLine("value of i is currently: {0}", i);
                bool isEven = ((i % 2) == 0);
                return isEven;
            });

            Console.WriteLine("Here are your even numbers:");
            foreach (int evenNumber in evenNumbers)
            {
                Console.Write("{0}\t", evenNumber);
            }
            Console.WriteLine();
        }
        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LambdaExpressionsMultipleParams
{
    #region SimpleMath (with delegates)
    public class SimpleMath
    {
        public delegate void MathMessage( string msg, int result );
        private MathMessage mmDelegate;

        public void SetMathHandler( MathMessage target )
        { mmDelegate = target; }

        public void Add( int x, int y )
        {
            if (mmDelegate != null)
                mmDelegate.Invoke("Adding has completed!", x + y);
        }
    }
    #endregion

    class Program
    {
        static void Main( string[] args )
        {
            // Register w/ delegate as a lambda expression.
            SimpleMath m = new SimpleMath();
            m.SetMathHandler(( msg, result ) =>
            { Console.WriteLine("Message: {0}, Result: {1}", msg, result); });

            // This will execute the lambda expression.
            m.Add(10, 10);
            Console.ReadLine();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是刘彦宏吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值