C# 事件与弱事件

前面三章中依次讲了C# 委托的基本概念C# Action和Func委托C# 委托之匿名方法和lambda表达式,基于以上内容,来说说事件

事件基于委托,为委托提供了一种发布/订阅机制。在Windows应用程序中,Button类提供了Click事件,这类事件就是委托。

在本章讲述中,采用示例GameDealer类和Consumer类来讲述事件,GameDealer类提供一个新游戏发布时的触发事件,Consumer类订阅该事件,获得新游戏的通知。

一、事件发布程序

GameDealer类基于事件提供一个订阅,GameDealer类用event关键字定义了类型为EventHandler<GameInfoEventArgs>的NewGameInfo事件。在NewGame方法中,触发NewGameInfo事件(注:?.是C#6.0后新增的空值传播运算符)

public class GameInfoEventArgs : EventArgs
    {
        public GameInfoEventArgs(string game)
        {
            Game = game;
        }

        public string Game { get; }
    }
public class GameDealer
    {
        public event EventHandler<GameInfoEventArgs> NewGameInfo;

        public void NewGame(string game)
        {
            Console.WriteLine($"GameDealer, new game {game}");

            NewGameInfo?.Invoke(this, new GameInfoEventArgs(game));
        }

    }

GameDealer类提供了EventHandler<GameInfoEventArgs>的NewGameInfo事件。作为约定,事件一般使用带两个参数的方法,其中一个参数是一个事件发送者对象,第二个参数是事件的相关信息,随事件类型而改变。

 public event EventHandler<GameInfoEventArgs> NewGameInfo;

泛型委托EventHandler<TEventArgs>,返回void,接受两个参数,分别是Object和T。

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

二、事件侦听器

Consumer类用作事件侦听器,这个类订阅了GameDealer类的事件,并定义了NewGameIsHere方法,该方法符合EventHandler<GameInfoEventArgs>委托的要求,委托的参数类型是object和GameInfoEventArgs:

 public class Consumer
    {
        private string _name;

        public Consumer(string name)
        {
            _name = name;
        }

        public void NewGameIsHere(object sender, GameInfoEventArgs e)
        {
            Console.WriteLine($"{_name}: Game {e.Game} is new");
        }
    }

现在连接事件发布程序和订阅器,使用GameDealer类的NewGameInfo事件,通过“+=”创建一个订阅。消费者Zhang San订阅了这个事件,接着消费者Li Si也订阅了这个事件,然后Zhang San通过“-=”取消了订阅:

static void Main(string[] args)
        {
            var dealer = new GameDealer();
            var zhangSan = new Consumer("Zhang San");
            dealer.NewGameInfo += zhangSan.NewGameIsHere;

            dealer.NewGame("Hello Game");

            var liSi = new Consumer("Li Si");
            dealer.NewGameInfo += liSi.NewGameIsHere;

            dealer.NewGame("Surprising Game");

            dealer.NewGameInfo -= zhangSan.NewGameIsHere;

            dealer.NewGame("Laughing Out Loud Game");

            Console.ReadKey();
        }

运行程序,新游戏Hello Game发布,Zhang San得到了通知,之后Li Si也注册了该订阅,所以Zhang San 和Li Si都得到了Surprising Game的通知。接着Zhang San取消的订阅,所以只有Li Si得到了通知:

三、弱事件

通过事件,可以直接发布程序和侦听器,但是如果后面不再使用侦听器,那么垃圾回收器就不能清空侦听器占用的内存,因为发布程序依然有一个引用,会针对侦听器触发事件。如上面的例子中,如果Li Si这个对象在程序中一直被使用,但是后面的代码已经不需要侦听器了,而Li Si又没有被标记为disposed,那么垃圾回收器就永远不会清空侦听器占用的内存。

可以通过弱事件模式,把WeakEventManager作为发布程序和侦听器之间的中间来解决这个问题。

WeakEventManager<T>在System.Windows程序集中,需要添加引用WindowsBase.dll:

使用弱事件,不需要改变事件发布器,只需修改使用者,使其实现接口IWeakEventListener。这个接口定义了方法ReceiveWeakEvent,在事件触发时,会在弱事件管理器中调用该方法。该方法的实现充当代理,调用NewGameInfo:

public class Consumer: IWeakEventListener
    {
        private string _name;

        public Consumer(string name)
        {
            _name = name;
        }

        public void NewGameIsHere(object sender, GameInfoEventArgs e)
        {
            Console.WriteLine($"{_name}: Game {e.Game} is new");
        }
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            NewGameIsHere(sender, e as GameInfoEventArgs);
            return true;
        }
    }

在Main方法中,连接发布器和侦听器,使用WeakEventManager<TEventSource, TEventArgs> 类的静态AddHandler和RemoveHandler方法建立连接:

 static void Main(string[] args)
        {
            var dealer = new GameDealer();
            var zhangSan = new Consumer("Zhang San");
            dealer.NewGameInfo += zhangSan.NewCarIsHere;
            WeakEventManager<GameDealer, GameInfoEventArgs>.AddHandler(dealer, "NewGameInfo", zhangSan.NewGameIsHere);

            dealer.NewGame("Hello Game");

            var liSi = new Consumer("Li Si");
            WeakEventManager<GameDealer, GameInfoEventArgs>.AddHandler(dealer, "NewGameInfo", liSi.NewGameIsHere);

            dealer.NewGame("Surprising Game");

            WeakEventManager<GameDealer, GameInfoEventArgs>.RemoveHandler(dealer, "NewGameInfo", zhangSan.NewGameIsHere);

            dealer.NewGame("Laughing Out Loud Game");

            Console.ReadKey();
        }

更多关于弱事件的详情可以看这个文章,指路:译文:C#中的弱事件(Weak Events in C#)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值