深入了解C#設(shè)計(jì)模式之訂閱發(fā)布模式
什么是Pub-Sub
發(fā)布訂閱是一種設(shè)計(jì)模式,它允許應(yīng)用程序組件之間進(jìn)行松散耦合。
其實(shí)訂閱發(fā)布設(shè)計(jì)中主要是發(fā)布者生成事件通道,用于在不了解任何訂閱者存在的情況下通知訂閱者。
當(dāng)然委托EventHandlers和Event關(guān)鍵字在此事件處理機(jī)制中擔(dān)任著重要的角色。下面我們來看看如何使用它們。
Pub和Sub的使用
首先我們看一個(gè)簡單地訂閱發(fā)布模式.
定義一個(gè)Action委托,無返回值.
namespace PubSubPattern
{
public class Pub
{
public Action OnChange { get; set; }
public void Raise()
{
if (OnChange != null)
{
//Invoke OnChange Action
OnChange();
}
}
}
class Program
{
static void Main(string[] args)
{
var p = new Pub();
p.OnChange += () => Console.WriteLine("Sub 1");
p.OnChange += () => Console.WriteLine("Sub 2");
p.Raise();
Console.WriteLine("Press enter !");
Console.ReadLine();
}
}
}
如上代碼我們創(chuàng)建了一個(gè)發(fā)布者,并且我們調(diào)用委托進(jìn)行創(chuàng)建我們匿名方法來訂閱。由于委托提供了多播功能,因此我們可以O(shè)nChange屬性上使用+=.
雖然說我們看著如上代碼執(zhí)行無誤,但是程序中仍然存在一些問題,如果使用=而不是+=,那么OnChange屬性中將會(huì)刪除第一個(gè)訂閱者。
由于OnChange是公共屬性,因此該類的任何外部用戶都可以進(jìn)行調(diào)用p.OnChange().
使用Event關(guān)鍵字的發(fā)布訂閱
下面我們來看看使用event關(guān)鍵字后的代碼
public class Pub
{
public event Action OnChange = delegate { };
public void Raise()
{
OnChange();
}
}
class Program
{
static void Main(string[] args)
{
Pub p = new Pub();
p.OnChange += () => Console.WriteLine("Sub 1");
p.OnChange += () => Console.WriteLine("Sub 2");
p.Raise();
Console.WriteLine("Press enter !");
Console.ReadLine();
}
}
通過如上代碼我們試著去解決我們第一處所說的問題,我們會(huì)發(fā)現(xiàn)使用event關(guān)鍵字后可以保護(hù)我們OnChange免受不必要的訪問。它不允許使用=也就是說他不允許直接進(jìn)行分配委托,因此我們現(xiàn)在可以避免使用=,從而避免應(yīng)用程序不必要的麻煩。
可能大家也會(huì)發(fā)現(xiàn)OnChange初始化為空委托delegate{}。這樣可以確保我們的OnChange永遠(yuǎn)不會(huì)為空。因?yàn)楫?dāng)我們其他進(jìn)行對他調(diào)用的時(shí)候我們可以在代碼中進(jìn)行刪除對他的非空檢查.
使用EventHandlers的發(fā)布訂閱
其實(shí)在訂閱發(fā)布中,發(fā)布者和訂閱者都不知道彼此的存在。有個(gè)EventHandler,它被稱為消息代理或者說事件總線,發(fā)布者和訂閱者都應(yīng)該知道它,它接收所有傳入的消息并且將它們進(jìn)行轉(zhuǎn)發(fā).
因此呢,在如下片段中我們使用EventHandler而不是用Action.
public delegate void EventHandler( object sender, EventArgs e )
默認(rèn)情況下,EventHandler將發(fā)送對象和一些事件參數(shù)作為參數(shù)。
public class MyEventArgs : EventArgs
{
public int Value { get; set; }
public MyEventArgs(int value)
{
Value = value;
}
}
public class Pub
{
public event EventHandler<MyEventArgs> OnChange = delegate { };
public void Raise()
{
OnChange(this, new MyEventArgs(1));
}
}
class Program
{
static void Main(string[] args)
{
Pub p = new Pub();
p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
p.Raise();
Console.WriteLine("Press enter !");
Console.ReadLine();
}
}
如上代碼中通過pub類使用通用的EventHandler,它觸發(fā)EventHandler OnChange時(shí)需要傳遞的事件參數(shù)類型,在上面代碼片段中為MyArgs
事件中的異常
我們繼續(xù)說一種情況.大家看如下代碼片段
public class MyEventArgs : EventArgs
{
public int Value { get; set; }
public MyEventArgs(int value)
{
Value = value;
}
}
public class Pub
{
public event EventHandler<MyEventArgs> OnChange = delegate { };
public void Raise()
{
OnChange(this, new MyEventArgs(1));
}
}
class Program
{
static void Main(string[] args)
{
Pub p = new Pub();
p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
p.OnChange += (sender, e) => { throw new Exception(); };
p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
p.Raise();
Console.WriteLine("Press enter !");
Console.ReadLine();
}
}
運(yùn)行如上代碼后,大家會(huì)發(fā)現(xiàn)第一個(gè)訂閱者已經(jīng)執(zhí)行成功了,第二個(gè)訂閱者引發(fā)了異常,而第三個(gè)訂閱者未被調(diào)用.這是一個(gè)很尷尬的事情.
如果說我們覺得如上的過程不是我們預(yù)期的,我們需要手動(dòng)引發(fā)事件并處理異常,這時(shí)候我們可以使用Delegate基類中定義的GetInvoctionList來幫助我們實(shí)現(xiàn)這些。
我們繼續(xù)看如下代碼
public class MyEventArgs : EventArgs
{
public int Value { get; set; }
public MyEventArgs(int value)
{
Value = value;
}
}
public class Pub
{
public event EventHandler<MyEventArgs> OnChange = delegate { };
public void Raise()
{
MyEventArgs eventArgs = new MyEventArgs(1);
List<Exception> exceptions = new List<Exception>();
foreach (Delegate handler in OnChange.GetInvocationList())
{
try
{
handler.DynamicInvoke(this, eventArgs);
}
catch (Exception e)
{
exceptions.Add(e);
}
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
}
class Program
{
static void Main(string[] args)
{
Pub p = new Pub();
p.OnChange += (sender, e) => Console.WriteLine("Sub 1.Value:" + e.Value);
p.OnChange += (sender, e) => { throw new Exception(); };
p.OnChange += (sender, e) => Console.WriteLine("Sub 2.Value:" + e.Value);
p.Raise();
Console.WriteLine("Press enter !");
Console.ReadLine();
}
}
Reference
https://github.com/hueifeng/DesignPatterns-Samples/tree/master/PubSubPattern
https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c
以上就是深入了解C#設(shè)計(jì)模式之訂閱發(fā)布模式的詳細(xì)內(nèi)容,更多關(guān)于c# 訂閱發(fā)布模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#連接SQL Server的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于C#連接SQL Server的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12
C#使用WebClient登錄網(wǎng)站并抓取登錄后的網(wǎng)頁信息實(shí)現(xiàn)方法
這篇文章主要介紹了C#使用WebClient登錄網(wǎng)站并抓取登錄后的網(wǎng)頁信息實(shí)現(xiàn)方法,涉及C#基于會(huì)話操作登陸網(wǎng)頁及頁面讀取相關(guān)操作技巧,需要的朋友可以參考下2017-05-05
C#使用NPOI將List數(shù)據(jù)導(dǎo)出到Excel文檔
這篇文章主要為大家詳細(xì)介紹了C#使用NPOI將List數(shù)據(jù)導(dǎo)出到Excel文檔,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02

