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);
委托的初始化
委托的初始化有以下三种方式:
- 直接使用函数赋值
- 使用匿名函数定义
- 使用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();
}
}
}