4000 C#之弱事件(Weak Event)的实现
2018-02-18 23:22
1546 查看
欢迎参与讨论,转载请注明出处。
本文转载自https://musoucrow.github.io/2018/02/18/weak_event_csharp/
实现弱事件自然需要用到弱引用,而弱引用的具体实现则是WeakReference,可以根据Delegate提供的Target作为弱引用对象,Method作为调用。
剩下的问题便是Delegate的参数问题了,很可惜Delegate似乎不支持作为泛型,但是Delegate的参数还是支持的。但即便是支持,也不方便作为多个参数来进行了。那么只能选择继承EventArgs了,EventArgs本身是个空类,一般做法是继承它然后自定义,这也是微软官方所推荐的做法。
以上便是弱事件的实现代码了,其实原理与Caller基本一致。接下来是演示:
输出结果为:
以上分别演示了静态方法、实例方法、匿名方法,其中静态方法和匿名方法需要手动调用Remove将之移除,如演示一般那样匿名方法便无从回收了,这点需要注意。如此弱事件便完成了,当然它带来了一定的性能损耗,这是无可避免的。也并未经过长久实践的磨砺,可以说只是一个原型罢了。
本文转载自https://musoucrow.github.io/2018/02/18/weak_event_csharp/
前言
最近使用C#开发项目时,发现一个会导致内存泄漏的陷阱——event里的成员并非弱引用,这样便会导致与event相关联的对象都不会被回收,从而导致内存泄漏。如此便很有必要实现一款弱事件(Weak Event)以解决此问题。分析
首先当然是找找是否存在现成的方案,答案是有的,不过很奇怪的是,该解决方案隶属于WPF,那么便没戏了。从网上来看也有不少各自的实现,不过个人对此都不算太满意,于是便打算自己造个轮子。实现弱事件自然需要用到弱引用,而弱引用的具体实现则是WeakReference,可以根据Delegate提供的Target作为弱引用对象,Method作为调用。
剩下的问题便是Delegate的参数问题了,很可惜Delegate似乎不支持作为泛型,但是Delegate的参数还是支持的。但即便是支持,也不方便作为多个参数来进行了。那么只能选择继承EventArgs了,EventArgs本身是个空类,一般做法是继承它然后自定义,这也是微软官方所推荐的做法。
实现
using System; using System.Reflection; using System.Collections.Generic; public class WeakEvent<TEventArgs> where TEventArgs : EventArgs { public delegate void Func(TEventArgs e); private static object[] ARGS = new object[1]; private class Unit { private WeakReference reference; private MethodInfo method; private bool isStatic; public bool IsDead { get { return !this.isStatic && !this.reference.IsAlive; } } public Unit(Func callback) { this.isStatic = callback.Target == null; this.reference = new WeakReference(callback.Target); this.method = callback.Method; } public bool Equals(Func callback) { return this.reference.Target == callback.Target && this.method == callback.Method; } public void Invoke(object[] args) { this.method.Invoke(this.reference.Target, args); } } private List<Unit> list = new List<Unit>(); public int Count { get { return this.list.Count; } } public void Add(Func callback) { this.list.Add(new Unit(callback)); } public void Remove(Func callback) { for (int i = this.list.Count - 1; i > -1; i--) { if (this.list[i].Equals(callback)) { this.list.RemoveAt(i); } } } public void Invoke(TEventArgs args=null) { ARGS[0] = args; for (int i = this.list.Count - 1; i > -1; i--) { if (this.list[i].IsDead) { this.list.RemoveAt(i); } else { this.list[i].Invoke(ARGS); } } } public void Clear() { this.list.Clear(); } }
以上便是弱事件的实现代码了,其实原理与Caller基本一致。接下来是演示:
using System; public class Obj { public void Do(EventArgs e) { Console.WriteLine("test"); } public static void StaticDo(EventArgs e) { Console.WriteLine("static"); } public static void Main(string[] args) { var a = new Obj(); var b = new Obj(); var weakEvent = new WeakEvent<EventArgs>(); weakEvent.Add(a.Do); weakEvent.Add(b.Do); weakEvent.Add(StaticDo); weakEvent.Add((EventArgs e) => Console.WriteLine("lambda")); a = null; weakEvent.Remove(StaticDo); GC.Collect(); weakEvent.Invoke(); Console.WriteLine(weakEvent.Count); } }
输出结果为:
lambda test 2
以上分别演示了静态方法、实例方法、匿名方法,其中静态方法和匿名方法需要手动调用Remove将之移除,如演示一般那样匿名方法便无从回收了,这点需要注意。如此弱事件便完成了,当然它带来了一定的性能损耗,这是无可避免的。也并未经过长久实践的磨砺,可以说只是一个原型罢了。
后记
类似这样的内存泄漏问题在开发过程中可有不少,尤其是有了GC的庇护下对此更为麻痹。一般需要定期使用专业工具进行检测,这也是优化的一环啊。相关文章推荐
- C# 实现事件(Event)演习代码
- c#控件实现类似c++中ocx控件功能,Event事件
- C#中的 .NET 弱事件模式 / The .NET weak event pattern in C#
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
- c#弱事件(weak event)
- c#弱事件(weakEvent)
- 用 C++ 实现 C# 中的 委托/事件 (2-delegate event functor)
- mouse_event实现鼠标事件
- C# 委托(Delegate) 事件(Event)应用详解
- C#里的委托和事件实现
- 封装 JMail 4.4 的 POP3 为 .Net 组件 (.dll 程序集),实现 "邮件(附件) 到达" 等 "事件,c#接收邮件
- C#中删除任意控件任意事件的事件列表的实现!
- c#中怎样实现当一个类实例的属性值改变时,触发事件调用另一个方法?
- C#中实现模拟鼠标事件
- C#实现自定义事件,用于监视变量变化
- c#中通过事件实现按下回车跳转控件
- c#中实现当一个类实例的属性值改变时,触发事件调用另一个方法
- C# 入门(10) 事件(event)
- C#学习日记24----事件(event)
- C#事件(event)解析