C#:包装方法调用的优雅方式

时间:2011-04-11 10:23:00

标签: c# decorator func

为相当模糊的标题道歉,但我想要实现的目标可能在代码中更好地说明。

我有一个WCF客户端。当我调用方法时,我想将每个调用包装在一些错误处理代码中。因此,我没有直接公开方法,而是在客户端类上创建了以下辅助函数:

    public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod)
    {
        try
        {
            return serviceMethod(decorator);
        }
        [...]
    }

客户端代码使用它如下:

service.HandleServiceCall(channel => channel.Ping("Hello"));

对Ping的调用很好地包含在一些试图处理任何错误的逻辑中。

这很好用,除了我现在需要知道在服务上实际调用哪些方法。最初,我希望只使用表达式树来检查Func<IApplicationService, T>但是没有走得太远。

最后,我选择了装饰模式:

    public T HandleServiceCall<T>(Func<IApplicationService, T> serviceMethod)
    {
        var decorator = new ServiceCallDecorator(client.ServiceChannel);
        try
        {
            return serviceMethod(decorator);
        }
        [...]
        finally
        {
            if (decorator.PingWasCalled)
            {
                Console.Writeline("I know that Ping was called")
            }
        }
    }

装饰者本身:

    private class ServiceCallDecorator : IApplicationService
    {
        private readonly IApplicationService service;

        public ServiceCallDecorator(IApplicationService service)
        {
            this.service = service;
            this.PingWasCalled = new Nullable<bool>();
        }

        public bool? PingWasCalled
        {
            get;
            private set;
        }

        public ServiceResponse<bool> Ping(string message)
        {
            PingWasCalled = true;
            return service.Ping(message);
        }
    }

它真的很笨重而且代码很多。 有更优雅的方式吗?

4 个答案:

答案 0 :(得分:3)

您可以使用表情,然后检查身体。

这样的东西
public T HandleServiceCall<T>(Expression<Func<IApplicationService, T>> serviceMethod)     
{         
    try         
    {          
        var func = serviceMethod.Compile();
        string body = serviceMethod.Body.ToString();
        return func(new ConcreteAppService()); 
    }        
    catch(Exception ex)
    {
        ...     
              }
}

答案 1 :(得分:2)

您是否考虑过使用面向方法?听起来就像你需要的那样。

包装异常和其他“元方法”功能可以写成与您的serviceMethods所做的“正交”方面。

关于AOP的一些一般信息:AOP in wikipedia

带容器的潜在解决方案:AOP with Windsor Castle

答案 2 :(得分:1)

以下是使用表达式树的简单示例:

public T HandleServiceCall<T>(Expression<Func<T>> serviceMethod)
{
    try
    {
        return serviceMethod();
    }
    finally
    {
        var serviceMethodInfo = ((MethodCallExpression)serviceMethod.Body).Method;
        Console.WriteLine("The '{0}' service method was called", serviceMethodInfo.Name);
    }
}

请注意,此示例假定serviceMethod表达式始终包含方法调用。

相关资源:

答案 3 :(得分:0)

是的,我相信您的代码已经过时了。

在为常见的安全处理代理包装代码方面,请查看here以获得一个不错的实现。使用它很容易:

using (var client = new Proxy().Wrap()) {
 client.BaseObject.SomeMethod();
}

现在您还需要访问方法名称 - 为此只需使用Environment.StackTrace。你需要在Marc Gravell的Wrap中添加堆栈。