In C#, thanks to the powerful GC mechanism, we develop programs very simply. Many times we only need to use them, and do not need to care about when resources are released. However, GC sometimes does not work the way we expect.
For example, I want to implement a real-time display of the current time in the title bar of a window. A more common approach is as follows:
var timer = new DispatcherTimer() { Interval = (1) }; += (_s, _e) => = (); ();
This approach looks very simple and direct, and it can honestly display and update time in the window as we designed it. However, experienced programmers know that there is a hidden danger here: this window will never be released. The simpler verification method is: manually close the window, call the() function, and find that the destructor will not be called.
Some people may ask: Isn’t there a omnipotent GC? Why is this window not released? The reason is also very simple. The Tick event of DispatchTimer contains a reference to the Window. When the window is closed, the DispatchTimer is still executing, so the Window cannot be released.
After knowing the reason, it is not difficult to solve it: in the Window shutdown event, just stop the Timer call. This method is indeed effective, but it seems not very elegant. It feels like I have returned to the C language era where I have to manually control the application and release, and there is no convenient feeling of "killing, not burying" under automatic GC management. So, is there a solution that we just use and release, and the answer isWeak event mode。
In weak event mode, event delegates only retain weak references to the object, so that GC can still reclaim the object. For example, for the above code, it can be modified as follows:
var timer = new DispatcherTimer() { Interval = (1) }; WeakEventManager<DispatcherTimer, EventArgs>.AddHandler(timer, "Tick", (_s, _e) => = ()); ();
Since Timer does not save strong references to Window, it will be recycled by GC after Windows is shut down.
There seems to be no problem now, but sensitive programmers will find that there is another hidden danger here: the DispatchTimer has not been released. Although we do not save the Timer's reference, in order to avoid it being recycled by GC, its reference will still be maintained internally and must be stopped explicitly. Here we can still use the weak event mode to manually stop the Timer when the callback object is sensed to be released. To implement this method, we must implement our own weak event manager:
public class DispatcherTimerManager : WeakEventManager { public static void Create(TimeSpan interval, EventHandler<EventArgs> handler) { var dispatcherTimer = new DispatcherTimer() { Interval = interval }; (dispatcherTimer, handler); (); } public static void AddHandler(DispatcherTimer source, EventHandler<EventArgs> handler) { (source, handler); } public static void RemoveHandler(DispatcherTimer source, EventHandler<EventArgs> handler) { (source, handler); } static DispatcherTimerManager current; static DispatcherTimerManager() { current = new DispatcherTimerManager(); SetCurrentManager(typeof(DispatcherTimerManager), current); } protected override ListenerList NewListenerList() { return new ListenerList<EventArgs>(); } protected override void StartListening(object source) { var timer = (DispatcherTimer)source; += OnSomeEvent; } protected override void StopListening(object source) { var timer = (DispatcherTimer)source; -= OnSomeEvent; (); } void OnSomeEvent(object sender, EventArgs e) { DeliverEvent(sender, e); } }
The code is relatively simple: when the callback object is sensed to be released, the StopListening function will be executed. We only need to rewrite the change function and add the stop Timer operation. Similarly, we can also implement an IObservable automatic management class based on weak event mode:
public static class ObservableDispatcher { public static void AddHandler<T>(IObservable<T> source, EventHandler<DataEventArgs<T>> handler) { if ( != ) throw new InvalidOperationException("Requires call on the main thread"); AnymousDispatcher<T>.AddHandler(source, handler); } public static void RemoveHandler<T>(IObservable<T> source, EventHandler<DataEventArgs<T>> handler) { AnymousDispatcher<T>.RemoveHandler(source, handler); } class AnymousDispatcher<T> : WeakEventManager { public static void AddHandler(IObservable<T> source, EventHandler<DataEventArgs<T>> handler) { var wrapper = new ObservableEventWrapper<T>(source); (wrapper, handler); } public static void RemoveHandler(IObservable<T> source, EventHandler<DataEventArgs<T>> handler) { var wrapper = new ObservableEventWrapper<T>(source); (wrapper, handler); } static AnymousDispatcher<T> current; static AnymousDispatcher() { current = new AnymousDispatcher<T>(); SetCurrentManager(typeof(AnymousDispatcher<T>), current); } protected override ListenerList NewListenerList() { return new ListenerList<DataEventArgs<T>>(); } protected override void StartListening(object source) { var wrapper = source as ObservableEventWrapper<T>; += wrapper_OnData; } void wrapper_OnData(object sender, DataEventArgs<T> e) { DeliverEvent(sender, e); } protected override void StopListening(object source) { var wrapper = source as ObservableEventWrapper<T>; -= wrapper_OnData; (); } } class ObservableEventWrapper<T> : IDisposable { IDisposable disposeHandler; public ObservableEventWrapper(IObservable<T> dataSource) { disposeHandler = (onData); } void onData(T data) { OnData(this, new DataEventArgs<T>(data)); } public event EventHandler<DataEventArgs<T>> OnData; public void Dispose() { (); } } }
limit:
Weak event mode is very useful, but I don't know why Microsoft has limited it to the WPF framework. From its implementation point of view, it should be called on the UI thread, but there is no explanation for its limitation on MSDN. I've tried calling it on a non-UI thread, which is also a weak event, but cannot trigger the StopListening function. I don't know if this will have any effect, but it's better to call it on the UI thread.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.