背景
從這篇文章開始我們來分析Prism框架中的事件聚合器EventAggregator,在開始這篇文章之前我們需要先申明這篇文章針對的是Prism8.x以及后續版本,應該在版本8以后針對這個事件聚合器做了BreakPoint,所以這里在開篇的時候需要特別的說明,如果需要了解之前的版本請參考之前寫過的博客C#基於消息發布訂閱模型中的上篇和下篇來了解更多背景信息。由於整個過程涉及到很多的內容,這個部分將通過多個篇幅來進行說明,閱讀的時候需要注意。
代碼分析
在進行整個代碼分析之前我們來看下整個類圖,這個有利於對整個結構有一個更加清晰的認識和理解。在了解完整個基礎結構后我們再來就其中非常重要的基礎概念及接口進行分析。
1 EventBase 基類
這個EventBase是整個PubSubEvent
的抽象基類,從類圖中我們可以看到這里面包含兩個重要的屬性Subscriptions和SynchronizationContext,前面一個Subscriptions
是一個List
///<summary>
/// Defines a contract for an event subscription to be used by <see cref="EventBase"/>.
///</summary>
public interface IEventSubscription
{
/// <summary>
/// Gets or sets a <see cref="SubscriptionToken"/> that identifies this <see cref="IEventSubscription"/>.
/// </summary>
/// <value>A token that identifies this <see cref="IEventSubscription"/>.</value>
SubscriptionToken SubscriptionToken { get; set; }
/// <summary>
/// Gets the execution strategy to publish this event.
/// </summary>
/// <returns>An <see cref="Action{T}"/> with the execution strategy, or <see langword="null" /> if the <see cref="IEventSubscription"/> is no longer valid.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
Action<object[]> GetExecutionStrategy();
}
這個SubscriptionToken 很好理解用於標識唯一的EventSubscription,這里我們重點來理解GetExecutionStrategy()這個方法,我們來看看這個方法的注釋:
Gets the execution strategy to publish this event.
顧名思義就是獲取當前PubSubEvent最終執行方的執行策略,這里我們來看看其泛型方法的實現。
/// <summary>
/// Provides a way to retrieve a <see cref="Delegate"/> to execute an action depending
/// on the value of a second filter predicate that returns true if the action should execute.
/// </summary>
public class EventSubscription : IEventSubscription
{
private readonly IDelegateReference _actionReference;
///<summary>
/// Creates a new instance of <see cref="EventSubscription"/>.
///</summary>
///<param name="actionReference">A reference to a delegate of type <see cref="System.Action"/>.</param>
///<exception cref="ArgumentNullException">When <paramref name="actionReference"/> or <see paramref="filterReference"/> are <see langword="null" />.</exception>
///<exception cref="ArgumentException">When the target of <paramref name="actionReference"/> is not of type <see cref="System.Action"/>.</exception>
public EventSubscription(IDelegateReference actionReference)
{
if (actionReference == null)
throw new ArgumentNullException(nameof(actionReference));
if (!(actionReference.Target is Action))
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidDelegateRerefenceTypeException, typeof(Action).FullName), nameof(actionReference));
_actionReference = actionReference;
}
/// <summary>
/// Gets the target <see cref="System.Action"/> that is referenced by the <see cref="IDelegateReference"/>.
/// </summary>
/// <value>An <see cref="System.Action"/> or <see langword="null" /> if the referenced target is not alive.</value>
public Action Action
{
get { return (Action)_actionReference.Target; }
}
/// <summary>
/// Gets or sets a <see cref="SubscriptionToken"/> that identifies this <see cref="IEventSubscription"/>.
/// </summary>
/// <value>A token that identifies this <see cref="IEventSubscription"/>.</value>
public SubscriptionToken SubscriptionToken { get; set; }
/// <summary>
/// Gets the execution strategy to publish this event.
/// </summary>
/// <returns>An <see cref="System.Action"/> with the execution strategy, or <see langword="null" /> if the <see cref="IEventSubscription"/> is no longer valid.</returns>
/// <remarks>
/// If <see cref="Action"/>is no longer valid because it was
/// garbage collected, this method will return <see langword="null" />.
/// Otherwise it will return a delegate that evaluates the <see cref="EventSubscription{TPayload}.Filter"/> and if it
/// returns <see langword="true" /> will then call <see cref="InvokeAction"/>. The returned
/// delegate holds a hard reference to the <see cref="Action"/> target
/// <see cref="Delegate">delegates</see>. As long as the returned delegate is not garbage collected,
/// the <see cref="Action"/> references delegates won't get collected either.
/// </remarks>
public virtual Action<object[]> GetExecutionStrategy()
{
Action action = this.Action;
if (action != null)
{
return arguments =>
{
InvokeAction(action);
};
}
return null;
}
/// <summary>
/// Invokes the specified <see cref="System.Action{TPayload}"/> synchronously when not overridden.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <exception cref="ArgumentNullException">An <see cref="ArgumentNullException"/> is thrown if <paramref name="action"/> is null.</exception>
public virtual void InvokeAction(Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
action();
}
}
看了這段代碼也十分清楚就是通過傳入一個Action<object[]>委托,當我們調用GetExecutionStrategy()方法的時候就會獲得當前委托,只不過這里外部傳入的不是直接的Action<object[]>委托,而是在這個基礎上面又重新封裝的一個稱之為IDelegateReference的接口,這里我們直接來看實現來看看這個這個封裝層到底有什么作用。
/// <summary>
/// Represents a reference to a <see cref="Delegate"/> that may contain a
/// <see cref="WeakReference"/> to the target. This class is used
/// internally by the Prism Library.
/// </summary>
public class DelegateReference : IDelegateReference
{
private readonly Delegate _delegate;
private readonly WeakReference _weakReference;
private readonly MethodInfo _method;
private readonly Type _delegateType;
/// <summary>
/// Initializes a new instance of <see cref="DelegateReference"/>.
/// </summary>
/// <param name="delegate">The original <see cref="Delegate"/> to create a reference for.</param>
/// <param name="keepReferenceAlive">If <see langword="false" /> the class will create a weak reference to the delegate, allowing it to be garbage collected. Otherwise it will keep a strong reference to the target.</param>
/// <exception cref="ArgumentNullException">If the passed <paramref name="delegate"/> is not assignable to <see cref="Delegate"/>.</exception>
public DelegateReference(Delegate @delegate, bool keepReferenceAlive)
{
if (@delegate == null)
throw new ArgumentNullException("delegate");
if (keepReferenceAlive)
{
this._delegate = @delegate;
}
else
{
_weakReference = new WeakReference(@delegate.Target);
_method = @delegate.GetMethodInfo();
_delegateType = @delegate.GetType();
}
}
/// <summary>
/// Gets the <see cref="Delegate" /> (the target) referenced by the current <see cref="DelegateReference"/> object.
/// </summary>
/// <value><see langword="null"/> if the object referenced by the current <see cref="DelegateReference"/> object has been garbage collected; otherwise, a reference to the <see cref="Delegate"/> referenced by the current <see cref="DelegateReference"/> object.</value>
public Delegate Target
{
get
{
if (_delegate != null)
{
return _delegate;
}
else
{
return TryGetDelegate();
}
}
}
/// <summary>
/// Checks if the <see cref="Delegate" /> (the target) referenced by the current <see cref="DelegateReference"/> object are equal to another <see cref="Delegate" />.
/// This is equivalent with comparing <see cref="Target"/> with <paramref name="delegate"/>, only more efficient.
/// </summary>
/// <param name="delegate">The other delegate to compare with.</param>
/// <returns>True if the target referenced by the current object are equal to <paramref name="delegate"/>.</returns>
public bool TargetEquals(Delegate @delegate)
{
if (_delegate != null)
{
return _delegate == @delegate;
}
if (@delegate == null)
{
return !_method.IsStatic && !_weakReference.IsAlive;
}
return _weakReference.Target == @delegate.Target && Equals(_method, @delegate.GetMethodInfo());
}
private Delegate TryGetDelegate()
{
if (_method.IsStatic)
{
return _method.CreateDelegate(_delegateType, null);
}
object target = _weakReference.Target;
if (target != null)
{
return _method.CreateDelegate(_delegateType, target);
}
return null;
}
}
看完了這段代碼后我們發現這個沒有什么特別的,重點是這個這個DelegateReference在構造函數中傳入keepReferenceAlive的bool類型參數,然后通過傳入true或者false來決定當前的DelegateReference對其引用的方式是直接引用還是弱引用(WeakReference)其它的沒有別的作用,這個也是很好理解的。至此整個EventSubscription的作用和脈絡我們都逐漸清晰,這個重要的概念的作用就是將特定PubSubEvent的訂閱方具體訂閱方法通過一個EventSubcription進行包裝,當我們執行PubSubEvent的Publish方法的時候我們能夠找到緩存在當前PubSubEvent中的訂閱方法並執行這個方法從而達到對當前事件的響應,到了這里思路是不是一下子就開闊起來了。
分析完這個EventSubscription這個基類,我們再來看看從這個基類派生的BackgroundEventSubscription
和DispatcherEventSubscription
這兩個子類(包括泛型子類)的作用,這里我們重點分析這幾個子類中最關鍵的部分,通過查看代碼后我們發現兩個子類都是通過重寫基類的虛方法達到定制化目的的。
/// <summary>
/// Invokes the specified <see cref="System.Action{TPayload}"/> synchronously when not overridden.
/// </summary>
/// <param name="action">The action to execute.</param>
/// <exception cref="ArgumentNullException">An <see cref="ArgumentNullException"/> is thrown if <paramref name="action"/> is null.</exception>
public virtual void InvokeAction(Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
action();
}
在這個BackgroundEventSubscription中最終執行委托是放在Task中進行的,而在DispatcherEventSubscription中則是在調用方當前的同步上下文中進行的(通過SynchronizationContext定義的Post方法實現),這兩個擴展實現了當前PubSubEvent的實際訂閱者執行的時候是在新的線程中進行還是和調用方的線程上下文保持一致,到了這里整個EventSubscription的作用全部理解完畢,整個過程中最重要的部分也到此完畢。
鑒於篇幅的原因,本篇文章就到此結束,下篇我們將講述從EventBase繼承的真正的PubSubEvent的具體實現以及最終的EventAggregator的實現,並最終進行一個完整的總結。