您的位置:首页 > 编程语言 > C#

【C#学习】delegate(委托) 和 event(事件)

2017-08-20 15:48 731 查看
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。在C#中方法不能作为参数直接传递,必须使用委托(用来委托方法)。delegate(委托)是一种特殊的引用类型,它将方法也作为特殊的对象封装起来,从而将方法作为变量、参数或者返回值传递。委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。使用一个委托有三个步骤: 

定义委托
实例化委托
将指定的方法添加到委托对象中
例子:

delegate int plus(int x, int y); // 1. 定义委托

static void Main(string[] args)
{
plus del_p;  // 2. 实例化委托

del_p = new plus(addition);  // 3. 将方法添加到实例化委托对象中

int n = del_p(1, 2);
Console.Write(n);
}

static int addition(int x, int y)
{
return x + y;
}

C#允许直接把方法名赋给委托,所以下面的三种写法也正确:

plus del_p = new plus(addition);
plus del_p2 = addition;
del_p = addition;


委托完全可以被当做普通类型对待,比如可以加减、赋值、构建数组。

委托数组

委托可以构建数组,意味着一组同样返回类型和参数类型的方法。
delegate int MathFunc(int x, int y);

static void Main(string[] args)
{
MathFunc[] fs = new MathFunc[]
{
substract,
addition,
product,
division
};

int n = fs[0](1, 2); // -1
}

static int division( int x, int y)
{
return x / y;
}

static int substract(int x, int y)
{
return x - y;
}

static int addition(int x, int y)
{
return x + y;
}

static int product(int x, int y)
{
return x * y;
}

委托的加减法/委托的多播(Multicasting of a Delegate)

一个委托对象可以封装多个方法,通过委托对象的合并(加法)实现。被合并的方法必须有相同的返回类型和参数类型。使用合并后的委托时,会依次将实参传入被合并的方法,最后的返回值以最后一个方法为准。
delegate int MathFunc(int x, int y);

static void Main(string[] args)
{
MathFunc f;
f = addition;
f += substract;
f += product;
int n = f(1, 2); // 2
}

static int division( int x, int y)
{
Console.WriteLine("division");
return x / y;
}

static int substract(int x, int y)
{
Console.WriteLine("substraction");
return x - y;
}

static int addition(int x, int y)
{
Console.WriteL
4000
ine("addition");
return x + y;
}

static int product(int x, int y)
{
Console.WriteLine("product");
return x * y;
}
上面的代码会打印出:

addition 
substraction 
product 

委托减法是加法的逆运算。如果被减委托中不含有减数委托,则不会对被减数造成任何影响,被减数减去自身为null,调用空委托会引发异常。总结下来委托加减法的几个结论:

委托加减null没有任何效果
委托减自身为null
减数如果没有包含在被减数中,则没有任何效果

在使用委托之前,应该判断委托是否为null。

传递委托

委托可以被作为以下方式传递

变量
方法参数
方法返回值
delegate int MathFunc(int x, int y);

static void Main(string[] args)
{
MathFunc f = getAddFunc();
int n = f(2, 3); // 5
int n2 = add(f, 3, 4); // 7
}

static int add(MathFunc f, int x, int y)
{
return f(x,  y);
}

static MathFunc getAddFunc()
{
return addition;
// or return new MathFunc(addition);
}









匿名方法(delegate方法)

使用 delegate 关键字直接定义方法,和实例化委托不同,不使用 new 关键字来创建委托对象,而是直接定义方法参数和方法体。匿名方法并不是真的没有名称,而是指程序员无需命名。C#在编译时会给方法生成一个方法定义(包含方法名和参数列表),该方法实际上是当前类型的一个私有静态方法。
delegate int MathFunc(int x, int y);

static void Main(string[] args)
{
MathFunc f = delegate (int x, int y)
{
return x + y;
};
int n = f(1, 2); // 3
}

Lambda表达式

从C# 3.0 开始,可以用 lambda表达式替换匿名方法表达式,直接将一个 Lambda表达式赋给委托对象。注意Lambda表达式不能作为语句,必须为右值。Lambda表达式的参数和返回类型必须和委托一致。
delegate int MathFunc(int x, int y);

static void Main(string[] args)
{
MathFunc f;
f = (int x, int y) =>
{
return x + y;
};
int n = f(1, 2); // 3
}
如果lambda表达式只有一个参数,则可以省略圆括号。Lambda表达式允许多种写法,以下写法等价:
something = (int x, int y) => { return x + y; };
something = (int x, int y) => x + y;
something = (x, y) => x + y;
something = (x, y) => { return x + y; };

Lambda表达式中没有用到的参数可以用 _ 代替。比如
something = (x, _) => x * 2;


在Lambda表达式和匿名delegate方法内部都可以访问主调函数的外部变量,比如:
MathFunc f;
string str = "str";
f = (x, y) =>
{
Console.Write(str); // 访问了外部的str
return x + y;
};
如果Lambda表达式或者匿名delegate方法的参数名和外部变量冲突,则无法通过编译。

委托发布和订阅

基于委托多播的特性,可以实现 “发布者/订阅者”模式。发布者对象有一个委托成员,保存一系列别的类的实例(订阅者)的方法,一旦发布者触发(调用)了委托,就会把所有注册过的方法都依次调用依次,通知订阅者。比如有一个红绿灯,一旦红绿灯变色,就会通知所有车辆和行人,此时可以把车辆和行人的响应方法注册到红绿灯对象的一个 public 委托成员 OnColorChange 里,当红绿灯调用 OnColorChange 时,就会通知所有车辆,调用他们的响应函数。由于订阅者的类型和响应方法千奇百怪,可以高度定制针对一个事件(比如红绿灯变色)的不同对象的不同响应函数。
由于委托成员 OnColorChange 是公有的,一旦别的程序将委托置为空,或其他值,委托先前的修改就前功尽弃了。为了防止这种事情发生,C# 提供了 event
来修饰(声明)委托成员。经过 event 修饰后,委托成员只能被 += 和 -= 修改,而不能用 = 修改。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: