C# 委托

it2023-10-06  65

委托

委托可以降低代码的重复率,并提高代码的封闭性 例如:

static List<int> Traverse(List<int> nums) { var list = new List<int>(); foreach (var num in nums) { if (num >= 10) {} list.Add(num); } } return list; //如果现在的需求是:遍历数组返回偶数呢?只需要在if判断中将 num>=10 改为 num%2==0,那么需求继续更改呢?,如果一次一次增加方法,那么代码的重复率太高,一次一次更改代码,代码的封闭性又太差。 //对代码进行更改,将if判断语句中的条件更改为方法。 static List<int> Traverse(List<int> nums) { var list = new List<int>(); foreach (var num in nums) { if (func(num)) { list.Add(num); } } return list; } static bool func(int num) { return num >= 10; } } //但是如何才能使func函数变化呢?接下来进行委托(匿名方法):1.声明委托类型 2.修改形参列表 3.传入委托 class Program { delegate bool Func(int num); //1.定义委托,声明委托类型 static Func BiggerThan10 = delegate (int num) { return num >= 10; }; //3.传入委托,定义不同的方法,要注意的是 delegate(int num)括号内参数要与第一步定义委托参数一致,还有由于这个方法最终是由 Traverse 调用的,而Traverse是静态方法,所以该方法也必须是静态 static Func Odd = delegate (int num) { return num % 2 != 0; }; static List<int> Traverse(List<int> nums,Func func) //2.修改形参列表 { var list = new List<int>(); foreach (var num in nums) { if (func(num)) { list.Add(num); } } return list; } static void Main(string[] args) { foreach (var num in Traverse(new List<int>() { 1, 2, 3, 4, 5, 10, 11 }, BiggerThan10))//3.传入委托 { Console.WriteLine(num); } foreach (var num in Traverse(new List<int>() { 3, 3, 4, 4, 5, 5, 6, 6 },Odd)) { Console.WriteLine(num); } Console.Read(); } } //此时代码实现了降低重复率,并进行了封闭,接下来进行代码的简化 static Func<int, bool> BiggerThan10 = delegate (int num) { return num >= 10; };//<type1,type2>1为输入值,2为返回值 static Func<int, bool> Odd = delegate (int num) { return num % 2 != 0; }; static List<int> Traverse(List<int> nums,Func<int,bool> func) { var list = new List<int>(); foreach (var num in nums) { if (func(num)) { list.Add(num); } } return list; }

多播委托

class Program { static Action<int> Print = i => Console.WriteLine(i); static Action<int> AddOnePrint = i => { i++; Console.WriteLine(i); }; static Action<int> All; static void Main(string[] args) { All = AddOnePrint; All += Print; All(1); Console.Read(); } }

报纸代码的思路过程

//发送报纸过程的简单架构实现 class Person//拥有属性姓名、构造方法、接收报纸类、为了接收并保存报纸对象因此还需要一个报纸属性 { public string name { get; set; } public Person(string name) { this.name = name; } public Newspaper news{ get; set; } public void SetNewspaper(Newspaper newspaper) { this.news = newspaper; } } class Publisher//拥有属性姓名、构造方法、发送报纸方法 { public string name { get; set; } public Publisher(string name) { this.name = name; } public void SendNewspaper(Newspaper newspaper) { var p1 = new Person("A"); var p2 = new Person("B"); var p3 = new Person("C"); p1.SetNewspaper(newspaper); p2.SetNewspaper(newspaper); p3.SetNewspaper(newspaper); } } class Newspaper//拥有属性标题、内容 { public string title { get; set; } public string Content { get; set; } } class Program { static void Main(string[] args) { var pub = new Publisher("出版社A"); pub.SendNewspaper(new Newspaper() { title = "标题", Content = "内容" });//实例化报纸对象并赋值,调用出版社对象的发送报纸方法 Console.Read(); } } //问题:代码不封闭。在出版社Publisher类中的SendNewspaper方法如果后续要发送第四个人、第五个人...就会不断修改 //解决办法:添加一个List用于存放person 的实例化对象。代码更改如下: class Publisher { public string name { get; set; } public Publisher(string name) { this.name = name; } public List<Person> persons = new List<Person>(); public void SendNewspaper(Newspaper newspaper) { persons.ForEach(person =>person.SetNewspaper(newspaper)); } } static void Main(string[] args) { var pub = new Publisher("出版社A"); pub.persons.Add(new Person("A")); pub.persons.Add(new Person("B")); pub.persons.Add(new Person("C")); pub.SendNewspaper(new Newspaper() { title = "标题", Content = "内容" }); Console.Read(); } //问题:如果这个时候公司(或者其他类型)也要收报纸,那么Publisher的SendNewspaper方法依旧不封闭 //解决办法:添加接口,接口有接收报纸、读报纸等公共方法。Company类、Person类实现接口。并将List改为接口类型,这样在main函数中向List中添加实例化对象后会进行向上转型。 interface INewspaper { void SetNewspaper(Newspaper newspaper); void ReadNewspaper(); } class Company:INewspaper{...} class Person:INewspaper{...} class Publisher { ... public List<INewspaper> subscribe = new List<INewspaper>(); ... } static void Main(string[] args) { Person A = new Person("A"); Person B = new Person("B"); Person C = new Person("C"); Company D = new Company("D"); Company E = new Company("E"); var pub = new Publisher("出版社A"); pub.subscribe.Add(A); pub.subscribe.Add(B); pub.subscribe.Add(C); pub.subscribe.Add(D); pub.subscribe.Add(E); pub.SendNewspaper(new Newspaper() { title = "标题", Content = "内容" }); pub.subscribe.ForEach(temp => temp.ReadNewspaper()); Console.Read(); } //简化代码,删掉借口,使用委托 class Publisher { public string name { get; set; } public Publisher(string name) { this.name = name; } public delegate void _Subscribes(Newspaper newspaper);//委托可以理解为系统给的一个类,这个类总有一些默认方法 public _Subscribes sub;//委托类的实例化对象 //这一段委托代码可以用Action代替: 【public Action<newspaper> sub;】 public void SendNewspaper(Newspaper newspaper) { sub(newspaper); } } class Program { static void Main(string[] args) { Person A = new Person("A"); Person B = new Person("B"); Person C = new Person("C"); Company D = new Company("D"); Company E = new Company("E"); var pub = new Publisher("出版社A"); pub.sub = A.SetNewspaper; pub.sub += B.SetNewspaper; pub.sub += C.SetNewspaper; pub.sub += D.SetNewspaper; pub.sub += E.SetNewspaper; Console.Read(); } } //委托存在的问题: //1.委托列表默认值为null,这样的话如果没有人订阅,即没有pub.sub = A.SetNewspaper; 那么就会有空指针异常。所以要进行null值检查 //ABCDE都订阅了,但是在执行委托链的时候,中间发生了异常,后面就执行不了了,因此需要try catch public void SendNewspaper(Newspaper newspaper) { //subscribe.ForEach(s => s.SetNewspaper(newspaper)); if(sub!=null) { foreach (Action<Newspaper> handler in sub.GetInvocationList()) { try { handler(newspaper); } catch (Exception ex) { Console.WriteLine("发生异常"); } } //sub(newspaper); } } //此时程序还不够健壮,即外部类可以调用委托,因此加上event关键字 public event Action<Newspaper> sub; catch (Exception ex) { Console.WriteLine("发生异常"); } } //sub(newspaper); } }

//此时程序还不够健壮,即外部类可以调用委托,因此加上event关键字

public event Action<Newspaper> sub;
最新回复(0)