Delegate
delegate是C#中很重要的语法。
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。
声明了delegate的属性都会继承自Delegate类。
使用单播委托类似C和C++的函数指针,但委托是面向对象和类型安全的(封装成delegate对象)。
使用多播委托类似观察者模式(发布/订阅模式)。实际上源码使用得最多的也是多播模式。但委托的通知实现方法绑定是基于函数签名(duck type)而非接口。相比传统的观察者模式实现更加灵活,有点像guava的Eventbus,而且实现更加优雅。
delegate不仅可以绑定外部方法,内部方法,静态方法。还能绑定匿名委托和lambda函数。可以使用 “=” 赋值委托方法,也可以使用“+=”和“-=”添加和去除委托方法。使用非常灵活方便。例子:
using System;
using System.Xml.Serialization;
namespace TestDelegateAndEvent
{
class TestDelagateAndEvent
{
public static void innerStaticSayHello(String name)
{
Console.WriteLine($"innerStaticSayHello:hello {name}");
}
public void innerSayHello(String name)
{
Console.WriteLine($"innerSayHello:hello {name}");
}
/*
* 声明delegate
*/
public delegate void SayHelloDelegate(String name);
/*
* 声明delegate对象
*/
public SayHelloDelegate sayHelloDelegate;
public event SayHelloDelegate sayHeloEvent;
public void activateSayHelloDelegate(String name)
{
sayHelloDelegate?.Invoke(name);
}
public void activateSayHelloEvent(String name)
{
sayHeloEvent?.Invoke(name);
}
}
class Test
{
public static void outerSayHello(String name)
{
Console.WriteLine($"outerSayHello:hello {name}");
}
static void Main(string[] args)
{
TestDelagateAndEvent obj = new TestDelagateAndEvent();
/*
* test delagate
*/
//delegate 能使用 "="
obj.sayHelloDelegate = obj.innerSayHello;
obj.sayHelloDelegate += TestDelagateAndEvent.innerStaticSayHello;
obj.sayHelloDelegate += Test.outerSayHello;
obj.sayHelloDelegate += (name) =>
{
Console.WriteLine($"Lambda:hello {name}");
};
//delegate 能作为对象直接调用invoke
Console.WriteLine("delegate 直接invoke");
obj.sayHelloDelegate("小明");
Console.WriteLine("delegate 在对象内部invoke");
obj.activateSayHelloDelegate("小明");
}
}
}
Event
event在C#中是一种特殊的委托。
在 C# Windows 窗体或 Web 应用程序中,控件事件的订阅和通知就是通过event来实现的。
event的使用和delegate类似。在 .NET Framework 类库中,事件基于 EventHandler 委托和 EventArgs 基类。我们可以通过继承EventArgs基类自定义自己的事件参数,并通过继承EventHandler 委托或者使用范型自定义自己的窗体事件。举MSDN上的例子:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Text;
namespace TestEvent
{
/*
* 自定义event 事件
*/
public class ShapeEventArgs : EventArgs
{
public double NewArea
{
get;
}
public ShapeEventArgs(double area)
{
this.NewArea = area;
}
}
/*
* shape抽象类
*/
public abstract class Shape
{
protected double _area;
public double Area
{
get => _area;
set => _area = value;
}
public event EventHandler<ShapeEventArgs> ShapeChanged;
public abstract void Draw();
protected virtual void OnShapeChange(ShapeEventArgs e)
{
ShapeChanged?.Invoke(this, e);
}
}
public class Circle : Shape
{
private double _radius;
public Circle(double radius)
{
_radius = radius;
_area = 3.14 * _radius * _radius;
}
public override void Draw()
{
Console.WriteLine("Write a Circle!");
}
protected override void OnShapeChange(ShapeEventArgs e)
{
base.OnShapeChange(e);
}
public void Update(double d)
{
_radius = d;
_area = 3.14 * _radius * _radius;
OnShapeChange(new ShapeEventArgs(_area));
}
}
public class Rectangle : Shape
{
private double _length;
private double _width;
public Rectangle(double length, double width)
{
_length = length;
_width = width;
_area = _length * _width;
}
public override void Draw()
{
Console.WriteLine("Write a Rectangle!");
}
protected override void OnShapeChange(ShapeEventArgs e)
{
base.OnShapeChange(e);
}
public void Update(double length,double width)
{
_length = length;
_width = width;
_area = _length * _width;
OnShapeChange(new ShapeEventArgs(_area));
}
}
public class ShapeContainer
{
private readonly IList<Shape> _list;
public ShapeContainer()
{
_list = new List<Shape>();
}
public void AddShape(Shape shape)
{
_list.Add(shape);
shape.ShapeChanged += handleShapeChanged;
}
private void handleShapeChanged(object sender, ShapeEventArgs e)
{
if (sender is Shape shape)
{
Console.WriteLine($"Received event. Shape area is now {e.NewArea}");
shape.Draw();
}
}
}
class Test
{
static void Main(string[] args)
{
ShapeContainer container = new ShapeContainer();
Circle circle = new Circle(54);
Rectangle rectangle = new Rectangle(12, 9);
container.AddShape(circle);
container.AddShape(rectangle);
// Cause some events to be raised.
circle.Update(57);
rectangle.Update(7, 7);
}
}
}
delegate和event的区别
event是特殊的delegate。
如果不基于EventHandler实现的event,跟delegate主要区别是event语法上多了一些限制。
1.event事件只能使用”+=“或”-=“进行订阅和取消订阅操作。不能进行“=”赋值操作。也即是说订阅了一个事件只能在通过”-=”进行取消订阅,除此之外无其他方式。
2.event事件触发只能通过事件所在对象内部触发。对象外部无法触发事件。而delegate能从对象外部对委托invoke。
还是使用委托的例子:
......
/*
* 声明delegate
*/
public delegate void SayHelloDelegate(String name);
/*
* 声明delegate对象
*/
public SayHelloDelegate sayHelloDelegate;
public event SayHelloDelegate sayHeloEvent;
......
static void Main(string[] args)
{
TestDelagateAndEvent obj = new TestDelagateAndEvent();
/*
* test delagate
*/
//delegate 能使用 "="
obj.sayHelloDelegate = obj.innerSayHello;
obj.sayHelloDelegate += TestDelagateAndEvent.innerStaticSayHello;
obj.sayHelloDelegate += Test.outerSayHello;
obj.sayHelloDelegate += (name) =>
{
Console.WriteLine($"Lambda:hello {name}");
};
//delegate 能作为对象直接调用invoke
Console.WriteLine("delegate 直接invoke");
obj.sayHelloDelegate("小明");
Console.WriteLine("delegate 在对象内部invoke");
obj.activateSayHelloDelegate("小明");
/*
* test event
*/
//event事件只能使用"+="或"-="
//test.sayHeloEvent = test.innerSayHello;
obj.sayHeloEvent += TestDelagateAndEvent.innerStaticSayHello;
obj.sayHeloEvent += Test.outerSayHello;
obj.sayHeloEvent += (name) =>
{
Console.WriteLine($"Lambda:hello {name}");
};
//event 只能在对象内部invoke
//test.sayHeloEvent("小明");
Console.WriteLine("event 在对象内部invoke");
obj.activateSayHelloEvent("小明");
.......
最后
msdn委托guide:
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/delegates/
msdn事件guide:
https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/events/