委托也叫代理,即把事情交给别人代办。C#中如果将一个方法委托给一个对象,对象即可全权代理该方法的执行。使用委托时首先要定义委托,声明委托所能代理方法的类型。
// 语法
<访问修饰符> delegate 返回类型 委托名();
委托没有具体的实现体,委托能够代表什么样的方法由它的返回值类型和参数列表决定。
// 示例
public delegate void testDelegate(string name);
定义了上述委托示例之后要使用委托,首先要实例化委托,也就是将其指向某个方法,即调用委托的构造函数,并将相关联的方法作为参数传递。然后通过调用 委托,执行相关方法的代码,实现委托。
// 使用委托对数组进行降序排列
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateSort
{
class Program
{
public delegate bool SortDelegate(int[] x); // 定义委托SortDelegate
static void Main(string[] args)
{
int[] arr = new int[] { 8, 9, 5, 3, 4, 6, 1 };
Console.WriteLine("排序前的数组是: ");
foreach (int i in arr) Console.Write("{0}", i); // 8953461
SortDelegate myDelegate; // 声明委托变量
myDelegate = new SortDelegate(Test.SortArray); // 实例化委托变量,委托Test的SortArray
myDelegate(arr); // 传递参数,调用委托排序
Console.WriteLine("");
Console.WriteLine("排序后的数组是:");
foreach (int i in arr) Console.Write("{0}", i); // 9865431
Console.ReadKey();
}
}
class Test
{
public static bool SortArray(int[] array )
{
// 定义方法SortArray用于按照降序排序
// GetUpperBound(int dimension): 获取数组中指定维度最后一个元素的索引。 dimension Int32 数组的从零开始的维度,其上限需要确定。
for (int i = array.GetUpperBound(0); i >= 0; i--) // 循环从后向前取出数组的元素
{
// 冒泡排序,将数值小的元素移到后面,直到所有的元素按照降序排列
for(int j = 0; j <= i; j++)
{
if(array[j] <= array[i]) // 判断数组前面的值是否小于后面的值
{
Swap(ref array[j], ref array[i]); // 交换二者的位置
}
}
}
return true; // 并非所有代码都有返回值,故加return true
}
static void Swap(ref int x, ref int y) // 交换方法
{
int temp = y;
y = x;
x = temp;
}
}
}
事件是C#中的一个高级概念,是操作发生时允许执行特定应用程序的代码的机制。事件要么在相关联的操作发生前发生(事前事件),要么在操作发生后发生(事后事件)。例如,当用户单击窗口中的按钮时,将引发一个事后事件,以允许执行特定于应用程序的方法。
类或对象可以通过事件向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”。在典型的C# Windows窗体或Web应用程序中,可以订阅由控件(如按钮和列表框)引发的事件。
定义和使用事件的步骤:
(1) 在一个类中声明关于事件的委托。
public delegate void 事件类型名称(object serder, EventArgs e);
事件类型名称建议用EventHandler结尾。如果想自定义事件的参数EventArgs,可以用EventArgs类派生自己的事件参数类,也可以没有参数。
(2)在类中声明事件,使用步骤(1)的delegate作为事件的类型。
public event 事件类型名称 事件名称;
(3)在类中需要引发事件的方法中,编写引发事件的方法。
事件名称(this, new EventArgs());
// 或者
if(事件名称 != null) 事件名称 (this, new EventArgs());
(4)订阅事件,当事件发生时通知订户。
带有事件的类实例.事件名称 += new 事件名称(事件处理方法名称);
(5)编写事件处理方法。
public void 事件处理方法(object sender, EventArgs e)
{
// 相关代码
}
(6)在适当的条件下触发事件,即需要调用步骤(3)中的引发事件的方法。
事件的定义和使用示例
假设设计一个高档热水器,通电加热到水温超过96℃的时候,扬声器会发出语音告诉你水的温度,液晶屏显示水温的变化,提示水快烧开了。定义一个类来代表热水器的类Heater,它有代表水温的字段temperature,有给水加热的BoilWater()方法,发出语音警报的MakeAlert()方法,显示水温的ShowMsg()方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HeaterEvent
{
class Program
{
static void Main(string[] args)
{
Heater heater = new Heater(); // 创建热水器对象heater
Alarm alarm = new Alarm(); // 创建报警器对象alarm
heater.BoilEvent += alarm.MakeAlert; // 给alarm的MakeAlert方法订阅事件
heater.BoilEvent += Display.ShowMsg; // 订阅静态方法
heater.BoilWater(); // 烧水,会自动调用订阅过的对象方法
Console.ReadKey();
/**
* Alarm: 嘀嘀嘀,水已经97度了!
* Display:水快烧开了,当前温度:97度。
* Alarm: 嘀嘀嘀,水已经98度了!
* Display:水快烧开了,当前温度:98度。
* Alarm: 嘀嘀嘀,水已经99度了!
* Display:水快烧开了,当前温度:99度。
* Alarm: 嘀嘀嘀,水已经100度了!
* Display:水快烧开了,当前温度:100度。
*/
}
}
public class Heater // 热水器类Heater
{
private int temperature; // 声明表示水温的字段
public delegate void BoilHandler(int param); // 声明关于事件的委托
public event BoilHandler BoilEvent; // 声明水要烧开的事件
public void BoilWater() // 烧水的方法
{
for(int i = 0; i <= 100; i++) // 循环表示水温的变化
{
temperature = i; // 设置水的温度
if(temperature > 96) // 水温超过96度,引发事件BoilEvent
{
/*if(BoilEvent != null)
{
// 如果有对象订阅
BoilEvent(temperature); // 调用所有订阅对象的方法
}*/
// 优化
BoilEvent?.Invoke(temperature); // 如果有对象订阅,调用所有订阅对象的方法
}
}
}
}
public class Alarm // 定义报警类
{
public void MakeAlert(int param) // 水快开时报警的方法
{
Console.WriteLine("Alarm: 嘀嘀嘀,水已经{0}度了!", param);
}
}
public class Display // 显示水温的显示类
{
// 静态方法 ShowMsg用于显示水温
public static void ShowMsg(int param)
{
Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
}
}
}