1、委托是什么?
)委托是一种动态调用方法的类型,属于引用类型;
)委托是对方法的抽象和封装。委托对象实质上代表了方法的引用(即内存地址、指针);
)委托通常是委托某个方法来实现具体的功能。当我们调用委托的时候,委托包含的所有方法将被执行。虽然在定义委托时与方法有些相似,但我们不能将其称为方法;
)与委托绑定的方法方法签名(即返回值类型和形参列表)与委托一致;
)委托本质是一个密封类,所以可以在命名空间下定义,也可以在类里面声明;
1、继承自MulticastDelegate
2、委托的构造函数,需要传递一个方法作为参数
3、委托的内部有三个方法Invoke(同步委托),BeginInvoke(异步委托),EndInvoke(阻塞)
2、委托五步法(适合窗体之间传值使用)
1、委托(方法类型)的定义:
访问权限 delegate 返回值类型 委托类型名(形参列表);
2、委托变量的定义;
委托类型 委托变量名;
3、具体方法的定义:
访问权限 返回值类型 方法名(形参列表);
4、委托方法的绑定:
委托变量名=方法名;or 委托变量名=new 委托类型(方法名)
5、调用:
委托变量名(实参列表);
例:A类调用B类的方法,A是调用者(1、2、5),B是响应者(3、4)
public delegate void DEL(); //1、委托(方法类型)的定义
internal class Program
{
static void Main(string[] args)
{
Person person = new Person();
person._del = Print; //4、委托方法的绑定
person._del =new DEL(Print);
person._del(); //5、调用
person._del.Invoke(); //同步委托调用
}
public static void Print() //3、具体方法的定义
{
Console.WriteLine("我是Program类的方法");
}
}
class Person
{
public DEL _del; //2、委托变量的定义
}
委托多窗体之间传值
建立两个窗体,主窗体和子窗体,UI界面分别如下
子窗体传值给主窗体,要在主窗体上显示值,调用主窗体的显示值的方法,子窗体是调用者(1、2、5),主窗体是响应者(3、4)
步骤:1、在子窗体定义委托类型,委托变量
2、主窗体创建与委托签名一致的方法,实例化子窗体,用对象.委托变量绑定主窗体方法
3、子窗体用调用此方法
子窗体代码
public delegate void DEL(string s); //1
public partial class ChirdForm : Form
{
public DEL _del; //2
public ChirdForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_del(textBox1.Text); //5
}
主窗体代码
public partial class MainForm : Form
{
public string aaa { get; set; }
ChirdForm chirdForm = new ChirdForm();
public MainForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
chirdForm._del = Say; //4
chirdForm.Show();
}
public void Say(string s) //3
{
this.textBox1.Text = s;
}
private void button2_Click(object sender, EventArgs e)
{
chirdForm.textBox2.Text = this.textBox1.Text; //给子窗体传值
}
}
3、多播委托
委托变量可以绑定多个签名一样的方法,调用时按顺序全部调用(+=)
也可以用(-=)逐一解绑
注:在移除的方法的时候,必须是同一个(委托变量)实例绑定的方法才能移除,每个lambda表达式在底层会生成不同的方法名的,看起来一样实际是不同的方法
static void Main(string[] args)
{
Person person = new Person();
//多播委托追加绑定
person._del = Print;
person._del += Ounter;
person._del += () => { Console.WriteLine("我是Program类的方法3"); };
//多播委托移除
person._del -= Print;
person._del -= Ounter;
person._del -= () => { Console.WriteLine("我是Program类的方法3"); }; //移除失败,这是另一个方法
person._del(); //5、调用
Console.ReadKey();
}
4、匿名委托(Lambda表达式)
匿名委托是指使用匿名方法注册在委托上,实际上是在委托中通过定义代码块来实现委托的作用
方法名使用关键字(delegate)代替
例:使用匿名委托实现长方形面积
public delegate double DEL(double lenth,double width); //定义委托类型(长、宽为参数,返回面积)
internal class Program
{
static void Main(string[] args)
{
Console.Write("请输入长方形长:");
double lenth=Convert.ToDouble(Console.ReadLine());
Console.Write("请输入长方形宽:");
double width = Convert.ToDouble(Console.ReadLine());
DEL del = delegate (double l, double w) //匿名委托
{
return l * w; //方法体实现面积计算并返回
};
double sum= del(lenth, width); //调用
Console.WriteLine($"长方形的面积:{sum}");
Console.ReadKey();
}
lambda表达式:对匿名委托的简写
参数列表(变量)=>表达式(或语句块{})
简写上面题目
public delegate double DEL(double lenth,double width); //定义委托类型(长、宽为参数,返回面积)
internal class Program
{
static void Main(string[] args)
{
Console.Write("请输入长方形长:");
double lenth=Convert.ToDouble(Console.ReadLine());
Console.Write("请输入长方形宽:");
double width = Convert.ToDouble(Console.ReadLine());
DEL del = (a,b)=> //lambda表达式
{
return a * b; //方法体实现面积计算并返回
};
double sum= del(lenth, width); //调用
Console.WriteLine($"长方形的面积:{sum}");
Console.ReadKey();
}
拆解:DEL del = (a,b)=> a * b ;
(a,b)这里a和b变量对应委托类型声明时的形参列表,都是double类型
语句块只有一句表达式可以直接写在=>后面
这里完成了三步:
2、委托变量的定义; 3、具体方法的定义; 4、委托方法的绑定
5、框架内置委托(Action<>/Func<>)
Action<>/Func<>是.NET Framework3.0时代的产物
1、Action<>
(1)Action<>是来自于System.RunTime的一个声明好的泛型委托,可以带有一个或者多个参数无返回值的委托,Action是无参无返回值委托类型
(2)最多支持16个入参,正常使用足够
2、Func<>
(1)Func<>是来自于System.RunTime的一个声明好的有返回值的委托,也可以有参数
(2)如果既然有参数也有返回值,前面是输入参数类型,最后面的作为返回值类型
(3)最多支持16个入参,正常足够使用
为什么要用框架内置委托?
(1)委托的本质是类,定义多个委托,其实就是新增了多个类,定义好的两个委托参数和返回值都是一致的,但是因为是不同的类,没有继承不能通用
(2)既然是系统框架给我们定义好了这两个委托,自然是希望我们在以后的开发中,都去使用这两个委托,这样就可以把委托类型做到统一
(3)那之前定义好的委托是去不掉的,这被称之为历史包袱
#region Action
Action act1 = () => { Console.WriteLine("我是无参无返回值"); };
Action<int> act2 = a => { Console.WriteLine("我是有参无返回值"); };
Action<int, string> act3 = (a, b) => { Console.WriteLine("我是有参无返回值"); };
#endregion
#region Func
Func<int> func1 = () =>
{
Console.WriteLine("我是无参有返回值");
return 1;
};
Func<int,string> func2 = a =>
{
Console.WriteLine("我是有参有返回值");
return "";
};
#endregion
6、委托作为参数传递
static void Main(string[] args)
{
Print((a,b)=>
{
a++;
b++;
},10,20);
Perit((a,b)=>
{
return a.ToString()+b.ToString();
},20,23);
}
//定义委托参数方法
public static void Print(Action<int,int> action,int a,int b)
{
action(a,b);
}
public static string Perit(Func<int,int,string> func,int a,int b)
{
return func(a,b);
}
注:委托,接口,值传递都和string一样,值传递到另一个方法赋值后,原始值不变
public delegate void DEL();
internal class Program
{
static void Main(string[] args)
{
DEL del=null;
Person person=new Person(del); //调用构造方法,给del绑定一个方法,但是由于委托有不变性,del还是为空
}
}
class Person
{
public Person(DEL _del)
{
_del = Print;
}
public void Print()
{
Console.WriteLine("llllllll");
}
}
7、匿名委托结合数组
创建People类,存放个体信息
创建Person类,把people类对象存入数组,并创建对应的方法
Main方法调用Person类确定数组元素个数,调用方法对数组元素进行处理
people类:
public class People //存放数组元素信息
{
public int ID { get; set; } //学号
public string Name { get; set; } //姓名
public double Gender { get; set; } //分数
public int PhoneNumber { get; set; } //电话号码
public void Print()
{
Console.WriteLine($"姓名:{Name},学号:{ID},分数:{Gender},电话号码:{PhoneNumber}");
}
}
person类:
public class Person
{
People[] peoples=null; //创建一个学生类
public int count = 0; //数组长度
public void Add(People pe) //添加元素的方法
{
if (count > 0) //判断数组是否为空
{
People[] item=new People[count];
for (int i = 0; i < count; i++) //原数组值存放临时数组
{
item[i] = peoples[i];
}
count++;
peoples = null; //清空原数组
peoples = new People[count]; //重置原数组长度
for (int i = 0; i < item.Length; i++) //临时数组放回原数组值
{
peoples[i]=item[i]; //这时peoples数组最后一个值是空的
}
}
else
{
count++;
peoples = new People[count]; //重置原数组长度,这时peoples数组最后一个值是空的
}
peoples[count - 1] = pe; //存放数组元素
}
public void Prin() //打印数组方法
{
foreach (People item in peoples)
{
item.Print();
}
}
//筛选方法,这里用到泛型委托,一个参数为People类对象,好处是调用时可以用lambda表达式去筛选条件打印
public void Scree(Action<People> action)
{
foreach(People item in peoples)
{
action(item);
}
}
public bool IsOrTrue(Func<People,bool> func) //判断是否有无
{
foreach (var item in peoples)
{
if (func(item)) //因为func返回的是一个bool值,所以在方法里充当条件使用
{
return true;
}
}
return false;
}
}
Main函数调用
internal class Program
{
static void Main(string[] args)
{
Person per = new Person();
per.Add(new People() { ID = 1, Name = "小明", Gender = 80, PhoneNumber = 15845454326 });
per.Add(new People() { ID = 2, Name = "小花", Gender = 58, PhoneNumber = 17485454321 });
per.Add(new People() { ID = 3, Name = "小猪", Gender = 88, PhoneNumber = 18858845328 });
per.Add(new People() { ID = 4, Name = "小狗", Gender = 99, PhoneNumber = 12545455824 });
per.Add(new People() { ID = 5, Name = "小猫", Gender = 25, PhoneNumber = 19945584355 });
per.Prin(); //打印全部学生信息
per.Scree(a=>
{
if (a.ID == 2) //找出学号2的学生
{
a.Print();
}
});
per.Scree(a =>
{
if (a.Gender >80) //找出分数大于80的学生
{
a.Print();
}
});
bool b1= per.IsOrTrue(a=> //班上是否有姓名是小明的学生
{
return a.Name == "小明";
});
Console.WriteLine(b1);
bool b2 = per.IsOrTrue(a =>
{
return a.Gender > 95; //班上是否有分数大于95的学生
});
Console.WriteLine(b2);
Console.ReadKey();
}
}
8、匿名委托结合列表
创建Student类,存放个体信息
Main函数调用系统方法
Student类
public class Student
{
public int ID { get; set; } //学号
public string Name { get; set; } //姓名
public string SubjectName { get; set; } //考试科目
public int ScoreValue { get; set; } //考试成绩
public Student(int iD, string name, string subjectName, int scoreValue)
{
ID = iD;
Name = name;
SubjectName = subjectName;
ScoreValue = scoreValue;
}
public void Print()
{
Console.WriteLine("学号:{0} 姓名:{1} 考试科目:{2} 考试成绩:{3}", this.ID, this.Name, this.SubjectName, this.ScoreValue);
}
}
Main函数调用
static void Main(string[] args)
{
List<Student> socre = new List<Student>()
{
new Student(1,"马老师","语文",90),
new Student(1,"马老师","数学",80),
new Student(1,"马老师","英语",70),
new Student(2,"达文西","语文",75),
new Student(2,"达文西","数学",56),
new Student(2,"达文西","英语",65),
new Student(3,"鸽鸽","语文",79),
new Student(3,"鸽鸽","数学",76),
new Student(3,"鸽鸽","英语",80),
};
foreach (Student s in socre)
{
s.Print();
}
//求各个同学总分与平局分
for (int i = 1; i <= 3; i++)
{
List<Student> students = socre.FindAll(t => t.ID == i);//找到匹配的学号
Console.WriteLine($"{students[0].Name}-总分:\t{students.Sum(t => t.ScoreValue):0.00}" );
Console.WriteLine($"{students[0].Name}-平均分:\t{students.Average(t => t.ScoreValue):0.00}" );
Console.WriteLine("===============================================================");
}
//求全部同学的数学平均分
double AvgHalcon = socre.Where(t => t.SubjectName == "数学").Average(t => t.ScoreValue);
Console.WriteLine($"数学平均分:\t{AvgHalcon:0.00}" );
Console.WriteLine("===============================================================");
//求全部同学的语文平均分
double AvgCSharp = socre.Where(t => t.SubjectName == "语文").Average(t => t.ScoreValue);
Console.WriteLine($"语文平均分:\t{AvgCSharp:0.00}" );
Console.WriteLine("===============================================================");
//查找低于60分的同学,打印输出
Console.WriteLine("低于60分的同学:");
socre.FindAll(t => t.ScoreValue < 60).ForEach(t=>t.Print());
Console.WriteLine("===============================================================");
//查询学号为2的同学数学考试成绩
Student MathSocre = socre.FirstOrDefault(t => t.ID == 2 && t.SubjectName == "数学");
Console.WriteLine($"学号为2的同学数学考试成绩:{MathSocre.ScoreValue}");
Console.WriteLine("===============================================================");
//求数学科目的最高分与最低分
int MaxMath = socre.Where(t => t.SubjectName == "数学").Max(t => t.ScoreValue);
int MinMath = socre.Where(t => t.SubjectName == "数学").Min(t => t.ScoreValue);
//打印输出最高分与最低分
Console.WriteLine($"数学科目的最高分:\t{MaxMath}");
Console.WriteLine($"数学科目的最低分:\t{MinMath}" );
Console.WriteLine("===============================================================");
Console.ReadKey();
}