4委托
委托
也是一种类型,特殊类型,初始化时需要一个方法支持,委托是记录方法信息的一种类型,它保存了方法的信息,调用委托的时候就是在调用实例化委托的方法。委托本质是一个方法指针,也就是方法的指针被封装了。
自定义委托
自定义委托的定义方法:
public delegate +返回值+委托名称+(参数类型 参数名);
如:
//定义一个无返无参的委托
public delegate void HelloDelegate();//这里的委托名HelloDelegate就相当于类型的名字,和string、int其一样的作用,表示HelloDelegate这个类型
//定义一个无返有参的委托
delegate void HelloDelegate2(string str);
//定义一个有返有参的委托
delegate string HelloDelegate3(string name);
使用方法:
1,委托初始化,需要一个和委托签名一样的方法来实例化,并且只需要写方法名即可,实际上就是创建了一个HelloDelegate2类型的实例,只不过这里面装的是一个方法
如:
HelloDelegate2 helloHelloDelegate2 = new HelloDelegate2(SayHello2);
2,调用委托,调用委托其实就是调用初始化委托的那个方法
格式:委托名.Invoke(参数);
helloHelloDelegate2.Invoke("");
或者
helloHelloDelegate2("")
其它使用方法1:将委托作为一个方法的参数,实现将一个方法作为参数传递
第一步,定义委托(无参无返回值的委托)
public delegate void Del();
第二步,将委托名放在要被要被调用该委托的参数内,并在被委托的函数内调用委托即可
public static void AA(Del del)/添加委托为参数类型
{
del();//方法内部调用该委托
}
第三步,声明一个Del()委托需要的函数类型
public static void BB()
{
Console.WriteLine("hello");
}
第四步,调用使用了委托Del为参数类型的方法AA,再传一个符合委托类型的方法即可
AA(BB);
BB为要执行的函数名,AA为需要委托为参数类型的函数,调用AA时会执行BB函数,因为AA的参数类型是委托Del的类型
也可以在AA的参数列表里多加一个参数,用于限制条件,如:
public static void AA(Del del,int money)//使用第二个参数进行判断
{
if (money>=100)
del();
}
自定义泛型委托的定义方法:
作用:使委托更加通用
delegate void GetSumDelegate<T>(T a, T b);
在实例化的时候指定类型即可
GetSumDelegate<int> getSum = new GetSumDelegate<int>(Getsum);
比如:
1、定义泛型委托
public delegate void GenericDelegate<D>(D d);
2、创建一个类,在这个类里面的方法的参数里使用这个类型的委托
class Person
{
public void PersonRun<P>(GenericDelegate<P> genericDelegate,P p)
{
genericDelegate(p);
}
}
3、在main函数里实例化并且传入符合该委托定义的方法即可
static void Main(string[] args)
{
Person person = new Person();
person.PersonRun(Run,5);
Console.ReadKey();
}
public static void Run<T>(T t)
{
Console.WriteLine($"泛型方法别调用了");
}
将匿名委托作为参数传递
1、定义一个无参数无返回值的委托
public delegate void DelValue();
2、在Person类的PersonRun方法里把上面定义的委托类型当作参数的类型,并且在方法里面调用传进来的委托
public void PersonRun(DelValue delValue)
{
delValue.Invoke();
}
3、一般都是定义好一个方法,
public static void Run()
{
Console.WriteLine("人们在跑步");
}
4、然后直接调用的时候传方法名即可
person.PersonRun(Run);
但是,也可以直接定义一个匿名委托,这样就不用再定义一个方法了,就像js的回调函数一样,f只不过js里面使用Function关键字,在C#里面使用delegate关键字而已
person.PersonRun(delegate () { Console.WriteLine("人们在跑步--传匿名委托"); });
或者是直接传lambda表达式
person.PersonRun(()=> { Console.WriteLine("人们在跑步--传lambda表达式"); });
多播委托
多播委托:在一个委托中加上多个方法
基本使用
1、创建一个无参无返的委托类型OrdDelegate
public delegate void OrdDelegate();
2、创建一个Person类,在该类中定义一个OrdDelegate类型的属性和一个调用该属性的方法
public class Person
{
public OrdDelegate ordDelegate { get; set; }
public void PersonRun()
{
ordDelegate();
}
}
3、在program类中创建一个符合委托类型的方法
public static void Test1()
{
Console.WriteLine("Test1");
}
public static void Test2()
{
Console.WriteLine("Test2");
}
public static void Test3()
{
Console.WriteLine("Test3");
}
4、在main函数中实例化person类并且给ordDelegate属性多赋值几个方法,或者减方法
Person person = new Person();
//加上方法
person.ordDelegate = Test1;
person.ordDelegate += Test2;
person.ordDelegate += Test3;
//减去方法
person.ordDelegate -= Test1;
//调用委托
person.PersonRun();//也可以直接用类名调用委托:person.ordDelegate();
因为减掉了Test1,所以只调用了Test2,和Test3方法,这里因为给ordDelegate属性加了几个方法,所以一次调用即可调用多个方法
事件委托
事件:事件就是相当于一个响应。
通过event关键字修饰委托类型的委托就是事件委托,事件可以在类的内部直接调用,但是无法在类的外部直接调用,在类外面只能把事件放在+=前面,然后给它赋值而已,但是不能调用,原理类似于属性,属性是用来保护字段的,一般字段都不希望被外部直接访问,同理,事件就非常类似于属性对私有字段的封装,事件本质上也是对委托的一种封装,这样能提供委托的安全性。
1、定义一个有参无返的委托EventDelegate类型
public delegate void EventDelegate(string msg);
2、创建一个Person类
public class Person
{
private string _userName;//字段
public Person(string userName)//构造函数
{
_userName = userName;
}
public event EventDelegate eventDelegate;//事件委托
public void Run()//调用事件的方法
{
eventDelegate(_userName);//调用事件,事件会自动帮我们调用委托
}
}
3、在main函数里面给实例化对象并且给事件赋值和调用person内部调用事件的方法
Person person = new Person("张三");//实例化方法
person.eventDelegate += Person_EventHandle;//给事件赋值
person.Run();//调用person类的Run方法调用事件
Person_EventHandle方法
private static void Person_EventHandle(string msg)
{
Console.WriteLine(msg);
}
系统(预定义)委托
系统预先定义好的委托就叫系统委托
作用
简化使用委托,使用委托的时候不需要重复定义,直接使用
分类
Action:没有返回值,参数0-16个
Func:必须有返回值,参数可以0-16个,返回值为最后一个参数
Predicate:限定了返回值必须是bool类型,用于判断式的委托
Comparison:返回值数字,用于计算
Action使用
初始化格式:
Action<参数类型和个数> 委托名 = new Action<参数类型和个数>(满足委托的方法);
如:
Action<string, int> actionDelegate = new Action<string, int>(SayHello);
SayHello方法如下:
public static void SayHello(string name, int age)
{
Console.WriteLine("你好,我叫" + name + "我今年" + age + "岁");
}
调用:actionDelegate.Invoke("张三",20);
Action使用实例
第一步:创建一个参数为一个委托的方法AA
public static void AA(Action<int, int, int> act)//参数为委托的方法
{
//执行传过来的方法,并且提供参数
act.Invoke(1, 2, 3);
}
第二步:为AA方法创建一个符合参数类型的方法Test
public static void Test(int a, int b, int c)
{
Console.WriteLine(""+a+b+c);
}
第三步:调用
AA(Test);
也可在使用lamdba表达式写一个符合委托类型的方法调用:
AA(( a, b, c) => { Console.WriteLine(a + b + c); });
Func使用
初始化格式:在泛型里写具体参数类型和个数的时候,最后一个参数就是返回值,如果只有一个参数,那么这个参数就是返回值
Func<参数类型和个数> 委托名 = new Func<参数类型和个数>(满足委托的方法);
一初始化:有返回值有参数的类型
Func<string, bool> funcDelegate2 = new Func<string, bool>(ResultName2);
,返回值类型为bool
ResultName2方法如下:
public static bool ResultName2(string naem)
{
return true;
}
二调用:
funcDelegate2.Invoke("xxx"))
初始化的时候也可以直接写一个lamdba表达式给它,如:
Func<string, bool> funcDelegate2 = new Func<string, bool>((a) => { return false; });
实现List的Find的方法
系统自带的find方法实际上循环集合,然后执行你传的lambda表达式或者符合它的委托的方法,不过注意在传递的时候返回值都是bool类型。
如:
List<int> intList = new List<int>() { 1, 2, 3, 4 };
int number = intList.Find(m => m == 2);
1、首先定义一个返回值为bool的泛型委托MyFindDelegate类型,方便传递方法
public delegate bool MyFindDelegate<T>(T t);
2、创建MyFind方法
public static T MyFind<T>(List<T> list, MyFindDelegate<T> myFindDelegate)
{
//根据反射创建泛型的对象实例
T t = (T)Activator.CreateInstance(typeof(T));
//循环遍历list集合
foreach (var item in list)
{
if (myFindDelegate(item))//根据传递进来的方法进行筛选,把符合条件的值赋值给t对象
{
t = item;
break;//找到了即可跳出循环
}
}
return t;
}
3、调用即可
List<int> intList = new List<int>() { 1, 2, 3, 4 };
int number = MyFind(intList,
delegate (int i)
{
return i == 2;
}
);//number值为2
也可以将这个MyFind方法定义为扩展方法,比较方便
实现List的FindAll的方法
1、首先定义一个返回值为bool的泛型委托MyFindDelegate类型,方便传递方法
public delegate bool MyFindDelegate<T>(T t);
2、在静态类里面创建扩展方法即可
public static List<T> MyFindAll<T>(this List<T> list, MyFindDelegate<T> myFindDelegate)
{
List<T> listT = new List<T>();
foreach (T item in list)
{
if (myFindDelegate(item))
{
listT.Add(item);
}
}
return listT;
}
3、调用即可
List<int> intList = new List<int>() { 1, 2, 3, 4 };
foreach (var item in intList.MyFindAll(m => m < 3))
{
Console.WriteLine(item);
}
这样就可以按照你想要的任意方法筛选了,只要筛选方法返回bool值即可,如,根据内容筛选
List<string> stringList = new List<string>() { "AA", "AB", "BB", "CC", "DD" };
foreach (var item in stringList.MyFindAll(m => m.Contains("A")))
{
Console.WriteLine(item);
}
用于List的RemoveAll的方法
很多方法都用到了委托,比如集合的RemoveAll,它就用到了Func<string,bool>这种参数的委托,所以在使用这个方法的时候可以传符合委托的方法进去,这样RemoveAll方法就会执行你写的逻辑,比如:
RemoveAll默认删除集合里的全部数据,但是我们可以通过传一个方法,让它只删除一个数据,如:
//只删除一个内容
List<string> myList = new List<string>();
myList.Add("a");
myList.Add("b");
myList.Add("c");
myList.Add("d");
//RemoveAll就可以传一个方法,因为内部也使用了委托
myList.RemoveAll(AA);
用于List的RemoveAll方法的参数的方法
public static bool AA(string str)
{
if (str == "a")
{
return true;
}
return false;//返回true表示全部删完,反之为不删
}
拓展使用,list实现排序筛选
我们可以使用委托,为List泛型集合写一些扩展方法,让其参数为委托类型,所以在调用的时候就可以写一个自己逻辑的方法作为参数传进来,让其实现自己的功能。
如:
\\自定义循环方法,调用的时候传符合委托的方法即可
public static void MyForeach<T>(this List<T> list, Action<T> action)
{
//循环输出list里的元素
for (int i = 0; i < list.Count; i++)
{
//执行用户传的方法
action(list[i]);
}
}
//判断集合中值是否相等
public static T MyFirstOrDefault<T>(this List<T> list, Func<T ,bool> action)
{
for (int i = 0; i < list.Count; i++)
{
//调用委托判断是否和集合当前的元素相等
if (action(list[i]))
{
return list[i];
}
}
//循环完没找到就返回默认值
return default(T);
}
//自己写的Where扩展方法
public static List<T> MyWhere<T>(this List<T> list, Predicate<T> pre)
{
List<T> newList = new List<T>();
foreach (var item in list)
{
if (pre(item))
{
newList.Add(item);
}
}
return newList;
}
使用上面的扩展方法
1:使用自定义的扩展方法进行排序筛选
List<string> myList = new List<string>() { "葡萄", "香蕉", "西瓜", "木瓜", "哈密瓜", "西瓜","香瓜" };
//lamdba实现查询
Console.WriteLine("请输入要查找的水果名");
string name = Console.ReadLine();
//查指定的水果,只能找到第一次匹配的项
//string jgname = myList.FirstOrDefault((a) => { return a == name; });
//使用会First报错
//调用自己写的扩展方法
//string jgname = myList.MyFirstOrDefault(a => name == a);
//Console.WriteLine(jgname);
//查指定的水果,只能找到所有匹配的项(精确查找)
//List<string> myList2 = myList.Where((a) => { return a == name; }).ToList();
//查指定的水果,只能找到所有匹配的项(模糊查找)1
//List<string> myList2 = myList.Where((a) => { return a.Contains(name); }).ToList();
//查指定的水果,只能找到所有匹配的项(模糊查找)2,并排序,OrderBy升序,OrderByDescending降序
//List<string> myList2 = myList.Where((a) => { return a.IndexOf(name) > -1; }).OrderByDescending((a) => { return a; }).ToList();
//foreach (var item in myList2)
//{
// Console.WriteLine(item);
//}
2,通过对象集合来使用上面的扩展方法
public class UserInfo
{
public string name { get; set; }
public int age { get; set; }
public string address { get; set; }
}
List<UserInfo> myList = new List<UserInfo>();
myList.Add(new UserInfo() { name = "李清照", age = 23, address = "宋" });
myList.Add(new UserInfo() { name = "黄月英", age = 18, address = "三国" });
myList.Add(new UserInfo() { name = "黄忠", age = 50, address = "三国" });
myList.Add(new UserInfo() { name = "黄某", age = 27, address = "三国" });
myList.Add(new UserInfo() { name = "黄某", age = 35, address = "三国" });
Console.WriteLine("输入要查找的姓名");
string name = Console.ReadLine();
Console.WriteLine("输入要查找的地址");
string address = Console.ReadLine();
//根据姓名模糊查询和地址精确查询并且根据年龄排序
//List<UserInfo> jgmyList = myList.Where((a) => { return a.name.Contains(name) && a.address == address; }).OrderBy((b) => { return b.age; }).ToList();
//循环遍历
//foreach (UserInfo item in jgmyList)
//{
// Console.WriteLine(string.Format("用户名:{0},年龄:{1},地址:{2}", item.name, item.age, item.address));
//}
//不输入条件查全表,输入姓名按照姓名查,输入地址按照地址查,都输入的话就表示精确查找
//List<UserInfo> jgmyList = myList.Where((a) =>
//{
// //返回一个false就表示没有满足条件,最后转换成集合,集合里也为空
// return (string.IsNullOrWhiteSpace(name) || a.name == name) && (string.IsNullOrWhiteSpace(address) || a.address == address);
//}).OrderBy((b) =>
//{
// return b.age;
//}).ToList();
//简写上一个匿名方法
List<UserInfo> jgmyList = myList.Where(a=>(string.IsNullOrWhiteSpace(name)||a.name==name)&&
(string.IsNullOrWhiteSpace(address)||a.address==address)).OrderBy(b=>b.age).ToList();
//满足条件的数据条数
//Console.WriteLine(jgmyList.Count);
////自定义循环
//jgmyList.ForEach((item) =>
//{
// Console.WriteLine(string.Format("用户名:{0},年龄:{1},地址:{2}", item.name, item.age, item.address));
//});
//调用自己写的扩展循环方法
jgmyList.MyForeach(item => Console.WriteLine(string.Format("用户名:{0},年龄:{1},地址:{2}", item.name, item.age, item.address)));
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构