C#中的delegate和event作用及区别

  • Post author:
  • Post category:其他




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/

示例代码:

https://github.com/rain-zhao/cs-test



版权声明:本文为rain_zhao_0102原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。