委托
委托可以降低代码的重复率,并提高代码的封闭性 例如:
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;