注册来自不同类的回调

时间:2016-02-13 13:04:05

标签: c# .net delegates action func

对于标准的请求服务器,我使用以下代码将请求与服务匹配并处理传入的请求。

的ServiceProvider

public class ServiceProvider
{
    public ServiceProvider()
    {
        Services = new Dictionary<Type, IService>
        {
            { typeof(FooRequest), new FooService() },
            { typeof(BarRequest), new BarService() },
        };
    }

    protected void OnRequestReceived(object request)
    {
        Services[request.GetType()].Process(request);
    }
}

FooService接口

public class FooService : IService
{
    public object Process(object request)
    {
        // Process request
    }
}

这就像一个魅力,但我想摆脱每个服务的一个方法(流程)。我尝试过使用Actions和Delegates,但我无法完成这个简单的结构。

基本上我的问题是:我如何从另一个类注册多个方法/回调并将它们存储在字典中以供我随时调用?

期望的结果(伪代码):

的ServiceProvider

public class ServiceProvider
{
    public ServiceProvider()
    {
        var fooService = new FooService();
        var barService = new BarService();

        Handlers = new Dictionary<Type, Action>
        {
            { typeof(FooRequestA), fooService.ProcessA },
            { typeof(FooRequestB), fooService.ProcessB },
            { typeof(BarRequest), barService.Process },
        };
    }

    protected void ProcessRequest(object request)
    {
        Handlers[request.GetType()].Invoke(request);
    }
}

FooService接口

public class FooService
{
    public object ProcessA(FooRequestA request)
    {
        // Process request A
    }

    public object ProcessB(FooRequestB request)
    {
        // Process request B
    }
}

解决方案的改进

使用以下方法,您可以简化定期请求 - 服务匹配代码:

public void RegisterHandler<TRequest>(Action<TRequest> function)
{
    Handlers.Add(typeof(TRequest), request => function.Invoke((TRequest) request));
}

这导致非常干净的使用:

RegisterHandler<FooRequestA>(request => fooService.ProcessA(request));

3 个答案:

答案 0 :(得分:1)

这是我能想到的最好的。 (虽然试图保持你所要求的接近......可能有更好的方法来实现你真正想做的事情)。我认为你在这个解决方案中失去了一点安全性:

<强> FooService接口

public class FooService
{
    public object ProcessA(FooRequestA request)
    {
        return null;
    }
    public object ProcessB(FooRequestB request)
    {
        return null;
    }
}

<强> BarService

public class BarService
{
    public void Process(BarRequest request)
    { }
}

<强>的ServiceProvider

public class ServiceProvider
{
    private readonly Dictionary<Type, Action<object>> Handlers;
    public ServiceProvider()
    {
        var fooService = new FooService();
        var barService = new BarService();

        Handlers = new Dictionary<Type, Action<object>>
        {
            {typeof(FooRequestA), request => fooService.ProcessA((FooRequestA)request)},
            {typeof(FooRequestB), request => fooService.ProcessB((FooRequestB)request)},
            {typeof(BarRequest), request => barService.Process((BarRequest)request)}
        };
    }

    protected void ProcessRequest(object request)
    {
        Handlers[request.GetType()].Invoke(request);
    }
}

由于您无法将方法组(如fooService.ProcessA)转换为Action,因此您需要将lambda添加到Dictionary<Type, Action<object>>并将请求作为参数提供。

答案 1 :(得分:1)

由于您将字典声明为<Type, Action>,因此无法将参数传递给.Invoke(object)方法 - Action不接受任何参数。您可能需要的是Dictionary<Type, Action<object>>。我说“可能”因为ProcessAProcessB方法返回object,所以Dictionary<Type, Func<object, object>>似乎更合乎逻辑。无论如何,我希望这只是一个教育问题,因为还有其他方法可以更加正确和有效地完成你所寻找的事情。

答案 2 :(得分:1)

您可以让服务决定他们可以处理哪些请求,而不是在ServiceProvider中注册单个方法。您可以创建一个包含以下方法的接口(我使用Request作为所有请求的基类来避免object参数):

public interface IService
{
    bool CanHandle(Request request);
    void Handle(Request request);
}

在注册表中,您注册服务并将请求路由到服务:

public class ServiceProvider
{
    private readonly IEnumerable<IService> _registeredServices = new IService[]
        {
             new FooService(), 
             new BarService();
        };

    protected void OnRequestReceived(Request request)
    {
        // Find first service that can handle the request
        var serviceForRequest = 
            _registeredServices.Where(x => x.CanHandle(request)).FirstOrDefault();
        if (serviceForRequest == null)
            throw new InvalidOperationException("No service registered that can handle request " + request.ToString();
        // Let the service handle the request
        serviceForRequest.Handle(request);
    }
}

此结构的优点是,每次服务可以处理新类型的请求时,您都不必更改ServiceProvider。转移到您的示例,您可以将ProcessB方法添加到FooService并更改班级中CanHandleHandle的实施,而无需更改注册。

您可以通过使用IoC容器或基于反射的实现来动态检测服务,而不是注册服务。在这种情况下,即使添加新服务,也无需更改ServiceProvider