C#中委托概念及使用方法详解
文章目录
委托的概念
多播委托
拖动按钮
前文提要:
💎超快速成,零基础掌握C#开发中最重要的概念
💎抽丝剥茧,C#面向对象快速上手
💎Winform,最友好的桌面GUI框架
💎C#泛型:高级静态语言的效率利器
委托的概念
委托这个名字取的神乎其神的,但实质是函数式编程,把函数作为参数传递给另一个参数。对于C语言程序员来说,就是把函数指针当作参数传递给另一个函数。
唯一需要注意的是,C#毕竟是强类型语言,用于委托的函数,也相当于变成了一种可以被传递的变量,所以在创建以及调用之前,需要声明其数据类型
delegate int Op(int a, int b);
1
这个委托是一种需要传入两个整型参数的函数,返回值也是整数。接下来对这个委托进行实例化,最终代码如下
int add(int a, int b)
{
return a + b;
}
var addTest = new Op(add);
void calc(Op func, int a, int b)
{
Console.WriteLine($"func({a},{b})={func(a,b)}");
}
calc(addTest, 2, 3);
delegate int Op(int a, int b);
事先说明一下,本文所有代码均在.Net6的顶级语句中实现,顶级语句需要把delegate声明放在最下面。
其中,add是一个十分质朴的函数,没什么可说的;addTest是一个内置了add了Op对象,其功能与add是相同的。
calc是一个以Op对象为参数的函数,在这个函数中,通过Op对象func,计算了另外两个参数a和b。
最后,调用了calc函数,将addTest作为参数,实质上是计算了add(2,3),并打印了这个结果。
func(2,3)=5
>“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
多播委托
所谓多播委托,就是一个委托中通过+=运算符添加多个函数。当然也可以通过-=运算符将原本添加的函数删除掉。
为了演示这个功能,将上述代码稍作更改。
int add(int a, int b){
Console.WriteLine($"{a}+{b}={a+b}");
return a + b;
}
int minus(int a, int b){
Console.WriteLine($"{a}-{b}={a-b}");
return a-b;
}
void calc(Op func, int a, int b)
{
func(a,b);
}
Op opTest = add;
opTest += minus;
opTest += add;
opTest += minus;
calc(opTest, 3, 4);
Console.WriteLine("减去一个minus");
opTest -= minus;
calc(opTest, 3, 4);
delegate int Op(int a, int b);
其中Op opTest=add的写法等价于Op opTest = new OpTest(add),但若省略new,则不可写为var opTest = add,这个时候没法进行类型推断。
输出结果为
3+4=7
3-4=-1
3+4=7
3-4=-1
减去一个minus
3+4=7
3-4=-1
3+4=7
由此可知,委托在调用的时候,会按照+=的先后顺序调用函数,并将最后一个调用的函数作为返回值。
而函数在委托中以栈的方式存放,-=会先减去后存入委托中的函数。
拖动按钮
源码地址:拖动按钮
多播委托在GUI编程中最为常用,尤其是拖动控件时。拖动控件的流程包括三个步骤
鼠标点击控件
鼠标拖动控件
鼠标松开控件
则对于一个控件来说,其绑定的事件会随着鼠标的点击情况而发生变化
0. 鼠标未点击时,控件需要响应鼠标点击事件
鼠标点击之后,控件需要响应鼠标拖动、鼠标松开的事件
鼠标拖动时,控件响应的事件并不发生变化
鼠标松开后,控件需要解绑拖动以及松开事件
接下来,实操一下,简单起见,GUI采用winForm,在新建项目之后,拖动一个按钮到窗口上,右键按钮->属性,可以更改一下名字和内容,然后点击右下角属性栏的小闪电,然后注册MouseDown事件,输入btnTest_MouseDown并按下回车之后,IDE会自动来到代码界面,并出现一个空的委托函数。
private void btnTest_MouseDown(object sender, MouseEventArgs e)
{
}
为了理解这个东西的作用,可以在解决方案资源管理器中找到Form1.Designer.cs文件,点进去之后可以看到下面这行代码
this.btnTest.MouseDown += new System.Windows.Forms.MouseEventHandler(this.btnTest_MouseDown);
1
换言之,btnTest.MouseDown就是一个多播委托,刚刚我们的行为,为其注册了一个名为btnTest_MouseDown的实现,尽管这个实现现在还是空的。
若想拖动一个控件,第一步就是按下鼠标,按下鼠标之后,需要再注册两个委托,分别再拖动鼠标和松开鼠标时起作用;而松开鼠标和按下鼠标的作用刚好相反,要求取消注册拖动事件,所以下面分别实现这三个功能。
private void btnTest_MouseDown(object sender, MouseEventArgs e)
{
btnTest.MouseMove += btnTest_MouseMove;
btnTest.MouseLeave += btnTest_MouseLeave;
}
private void btnTest_MouseLeave(object sender, EventArgs e)
{
btnTest.MouseMove -= btnTest_MouseMove;
btnTest.MouseLeave -= btnTest_MouseLeave;
}
private void btnTest_MouseMove(object sender, MouseEventArgs e)
{
int dh = btnTest.Height / 2;
int dw = btnTest.Width / 2;
btnTest.Top = MousePosition.Y - this.Top - dh;
btnTest.Left = MousePosition.X - this.Left - dw;
}
上面需要注意一点,MouseLeave和MouseMove, MoseDown是不同类型的委托,故而创建函数的参数类型是不同的。
btnTest.Top为按钮顶端距离窗口顶端的距离;MousePosition.Y表示鼠标距离屏幕顶端的距离;this.Top表示窗口顶端距离屏幕顶端的位置,最后再减去一个按钮高度的一半,相当于是把按钮的中心移动到鼠标光标处。这种逻辑过于简单粗暴,实际工作时不会用到,之所以这么写是因为简单。
相关文章
- C#之语言详述
- C# 序列化与反序列化之xml对属性或者字段的子类化的子对象进行序列化的解决方案
- C#子线程中更新ui-----c# 多线程多文件批量下载
- c#打开和关闭外部程序
- C#测试web服务是否可用
- [C#] c# 验证手机号码 最新的17手机号
- C#.NET常见问题(FAQ)-如何声明list的多维数组
- C#.NET常见问题(FAQ)-如何设置控件水平对齐,垂直对齐
- 编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]
- C# 获取今天,昨天,上周,下周,上月,下月等等一些日期格式
- 时间戳转为C#格式时间
- C# DataTable中返回列中的最大值
- C# Foreach语句
- C++与C#有关对库(动态库dll,静态库.lib)文件的调用
- C#中的DataSet、string、DataTable 、对象转换成Json
- C# 委托多播实例【通俗易懂】
- C# System.Windows.Forms.Application.DoEvents()
- C# 操作 MySql
- C#窗体设计上机实验报告四