C#语言中委托(delegate)可以实现类似C/C++的函数指针的作用,而事件(event)实现了发布订阅设计模式,可以用于实现对特定动作进行一系列响应。在日常开发中,两者还经常结合使用,例如Winform中就使用了委托和事件,实现为控件注册回调函数的机制。
如果熟悉C/C++我们应该了解函数指针,C#语言中没有函数指针,但给出了委托(delegate)的概念。委托可以看作一种安全的函数指针对象,它是一个引用类型,能够将函数的调用包装为变量。此外,委托比函数指针更加灵活,委托不仅可以包装一个函数,还可以包装一组函数实现组合委托。下面代码实现了一个简单的回调函数机制。
delegate void Callback();
class Program
{
static void Main()
{
void foo()
{
Console.WriteLine("程序运行成功!");
}
Callback cb = new Callback(foo);
loadData(cb);
}
static void loadData(Callback cb)
{
Console.WriteLine("加载数据中...");
cb();
}
}
上面例子代码中,我们首先声明了一个委托类型Callback
。委托的声明使用delegate
关键字,后面部分则和函数声明非常类似,实际上它规定了委托可以关联的函数签名。在Main
函数中,我们首先定义了一个局部函数foo
,然后创建委托对象cb
,并将局部函数的函数名作为参数传入其中。最终,我们将委托对象传入了另一个方法中,此时可以直接使用类似调用函数的方式调用委托对象cb
,函数foo
会被触发执行。
上面代码我们使用了一个局部函数创建委托,实际上实例方法、静态方法都可以用来创建委托。
// 使用实例方法创建委托
Callback c1 = new Callback(demoClass.Foo);
// 使用静态方法创建委托
Callback c2 = new Callback(DemoClass.Bar);
更进一步,其实new Callback()
也可以省略掉,简化的语法如下。
// 使用实例方法创建委托
Callback c1 = demoClass.Foo;
// 使用静态方法创建委托
Callback c2 = DemoClass.Bar;
实际上委托不仅能包含一个函数调用,也可以包含多个,这被称为组合委托。两个委托类型可以使用+
运算符生成一个新的组合委托。
Callback c1 = DemoClass.Foo;
Callback c2 = DemoClass.Bar;
Callback c3 = c1 + c2;
调用组合委托c3
时,会依次调用c1
和c2
。
委托也支持+=
和-=
运算符。
Callback c1 = DemoClass.Foo;
c1 += DemoClass.Bar;
c1 -= DemoClass.Foo;
这里要注意,委托类似于string
,是一个不可变对象,然而C#又为委托提供了+=
和-=
运算符,大家不要感到迷惑,实际上这里是生成了新的委托对象,而非在原来的委托对象上修改。
初始化委托时我们需要传入一个函数,然而如果这个函数仅用于初始化委托,单独定义一个函数的写法就比较麻烦了。C#2.0引入了匿名函数语法,用于简化这类代码的编写。
Callback c = delegate ()
{
Console.WriteLine("hello");
};
上面代码中,Callback
是一个委托类型,我们直接使用了一个匿名方法对其进行了初始化。匿名方法也是使用delegate
关键字修饰,且不需要设定方法名(毕竟这个概念就叫做匿名方法),小括号内可以指定方法的参数,匿名方法声明中不需要指定返回值,但如果委托具有非void
的返回值,匿名方法的方法体必须返回相同类型的数据。
匿名方法的另一个特性是它可以访问其外部作用域内的变量,比如下面代码中,匿名方法体内就可以访问外部的变量x
。
int x = 1;
Callback c = delegate ()
{
Console.WriteLine("x={0}", x);
};
实际上匿名方法的编写还是有一些麻烦,C#3.0引入了lambda表达式,能够进一步简化匿名方法的写法。实际开发中,绝大多数都是使用lambda表达式,而非匿名方法的写法。
Callback c = () =>
{
Console.WriteLine("hello");
};
lambda表达式使用() => { }
写法指定,用法和匿名方法一致。
C#中事件可以实现发布订阅设计模式,简单来说就是注册一些回调函数,满足特定条件时回调函数执行,事件需要结合委托来实现。下面例子代码实现了事件的声明和使用。
delegate void Callback();
class Demo
{
public event Callback callback;
public void foo()
{
callback();
}
}
class Program
{
static void Main()
{
Demo demo = new Demo();
demo.callback += () =>
{
Console.WriteLine("hello");
};
demo.foo();
}
}
代码中,我们首先声明了一个委托Callback
,类Demo
中我们使用event
关键字声明了一个事件,注意声明事件时需要使用委托类型,对应类型的委托会注册到事件中。最后在Main
方法中,我们实例化了这个类,并使用+=
运算符为事件注册回调函数。调用foo
方法后,回调函数被执行。