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)));
posted @ 2021-11-01 19:00  青仙  阅读(76)  评论(0编辑  收藏  举报