我最近在採訪中被問到,如果我們沒有活動和代表,我們如何實現沒有代表和活動的發佈者和訂閱者模型的相同功能。什麼是在C#中的事件和代表的替代?
你能用一個例子來解釋我嗎?它會非常有幫助,並且會幫助別人嗎?
我最近在採訪中被問到,如果我們沒有活動和代表,我們如何實現沒有代表和活動的發佈者和訂閱者模型的相同功能。什麼是在C#中的事件和代表的替代?
你能用一個例子來解釋我嗎?它會非常有幫助,並且會幫助別人嗎?
而不是使用委託,訂戶可以實現一個接口ISubscriber
,它具有方法SomeEvent
,並將其自身傳遞給發佈者(具有簽名Subscribe(ISubscriber subscriber)
的方法)。然後發佈者將此參考存儲到訂閱者,並在必要時致電subscriber.SomeEvent
。
喜歡的東西:
public interface ISubscriber
{
void SomeEvent(object publisher, object data);
}
public class SomePublisher
{
private readonly HashSet<ISubscriber> subscribers = new HashSet<ISubscriber>();
public void Subscribe(ISubscriber subscriber)
{
subscribers.Add(subscriber);
}
public void Unsubscribe(ISubscriber subscriber)
{
subscribers.Remove(subscriber);
}
public void DoSomething()
{
// Do something
// Then
foreach (var subscriber in subscribers)
{
object data = "Some data";
subscriber.SomeEvent(this, data);
}
}
}
需要注意的是發佈/訂閱的這種模式並不侷限於單一的「事件」:ISubscriber
可以有相應的多個「事件」多種方法。唯一的問題是,如果接口中有多個「事件」,訂閱者必須「訂閱」所有事件(必須具有所有事件的方法)。因此,如果有在ISubscriber
是一個OnAdded
和OnRemoved
方法,實現ISubscriber
類都必須有兩種方法(顯然,他們可能是空的存根什麼都不做)
我要補充的是,在結束時,學員可以「模擬「通過單一方法的接口,事件可以被認爲是List<somedelegatetype>
,所以事件可以被認爲是List<ISomeInterface>
。 Java的,例如沒有代表和使用接口與代替他們的一個方法(例如,見Java Delegates?)
發佈/訂閱的最簡單的實現:
// Universal interface for all subscribers:
public interface ISubscriber<TEvent>
{
void HandleEvent(object sender, TEvent ev);
}
// Universal publisher, can be used directly with generic argument
public class Publisher<TEvent>
{
protected ISet<ISubscriber<TEvent>> _subscribers = new HashSet<ISubscriber<TEvent>>();
public void Publish(TEvent ev)
{
foreach (var sub in _subscribers)
{
sub.HandleEvent(this, ev);
}
}
public void Subscribe(ISubscriber<TEvent> subscriber)
{
_subscribers.Add(subscriber);
}
public void Unsubscribe(ISubscriber<TEvent> subscriber)
{
_subscribers.Remove(subscriber);
}
}
// Or can be inherited to encapsulate any sort of logic
public class RandomIntegerPublisher : Publisher<int>
{
private readonly Random _random = new Random();
public void Publish()
{
Publish(_random.Next());
}
}
// Example subscriber, which can even implement multiple ISubscriber interfaces
public class ExampleSubscriber : ISubscriber<int>, ISubscriber<string>
{
public void HandleEvent(object sender, int ev)
{
Console.WriteLine($"Integer event: {ev}");
}
public void HandleEvent(object sender, string ev)
{
Console.WriteLine($"String event: {ev}");
}
}
void Main()
{
var subscriber = new ExampleSubscriber();
var randomIntegerPublisher = new RandomIntegerPublisher();
randomIntegerPublisher.Subscribe(subscriber);
var stringPublisher = new Publisher<string>();
stringPublisher.Subscribe(subscriber);
randomIntegerPublisher.Publish();
randomIntegerPublisher.Publish();
randomIntegerPublisher.Publish();
stringPublisher.Publish("Hello World!");
}
輸出:
Integer event: 1547238746
Integer event: 844169413
Integer event: 673377792
String event: Hello World!
它不是很靈活,因爲使用面向發佈者/訂閱者模式實現的OOP和爲每種類型的發佈者和每個特定訂閱者創建所需的類,但它顯示了主要思想,可以通過改進你自己在很多方面。
感謝Yeldar爲您提供的解決方案,它確實有幫助。我沒有足夠的積分來解決您的解決方案 –
謝謝@ xanatos此示例非常有用,我一直在尋找 –