c#中的事件和代理的替代方法是什么?

时间:2017-05-05 08:45:28

标签: .net events c#-4.0 delegates

我最近在接受采访时被问到,如果我们没有活动和代表,我们如何在没有代表和活动的情况下实现Publisher和Subsriber模型的相同功能。

你能用一个例子向我解释一下它会非常有用并且也会帮助其他人吗?

2 个答案:

答案 0 :(得分:6)

订阅者可以实现具有方法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);
        }
    }
}

请注意,此发布商/订阅者模式不限于单个&#34;事件&#34;:ISubscriber可能有多个方法对应多个&#34;事件&#34;。唯一的问题是如果有多个&#34;事件&#34;在界面中,订户必须&#34;订阅&#34;所有事件(必须有所有事件的方法)。因此,如果OnAdded中有OnRemovedISubscriber方法,那么实现ISubscriber的类必须同时具有这两种方法(显然它们可能是无效的空存根)

我补充一点,最后,代表们可以&#34;模拟&#34;通过具有单个方法的接口,可以将事件视为List<somedelegatetype>,因此可以将事件视为List<ISomeInterface>。例如,Java没有委托,并使用单一方法代替接口(参见例如Java Delegates?

答案 1 :(得分:3)

发布者/订阅者最简单的实现:

// 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用于发布者/订阅者模式实现,并且为每种类型的发布者和每个特定订阅者创建了必需的类,因此它不是非常灵活,但它显示了主要想法,可以通过多种方式自行改进。