简体   繁体   English

使用泛型来封装常见的方法工作

[英]Using Generics to encapsulate common method work

I've got a number of WebService methods that all include some very boilerplate code of wrapping the actual work in a try/catch/finally and performing the same tasks in the catch/finally. 我有许多WebService方法都包含一些非常的样板代码,用于将实际工作包装在try / catch / finally中,并在catch / finally中执行相同的任务。 So as a way to encapsulate all of the shared catch/finally stuff I wrote a simple generic. 因此,作为一种封装所有共享catch / finally内容的方法,我编写了一个简单的泛型。

This works and really eliminates a bunch of repetitive code but feels klunky, and the syntax is very obtuse. 这有效并且真正消除了一堆重复的代码,但感觉很笨,而且语法非常迟钝。 Every time I come back to this my brain gets twisted trying to figure it out (a clear sign it is not a good design). 每次我回到这里,我的大脑都会扭曲,试图找出它(一个明显的标志,这不是一个好的设计)。 I'm looking for feedback on whether this is a crazy thing to do, and if there is a better way to approach it. 我正在寻找关于这是否是一件疯狂事情的​​反馈,以及是否有更好的方法来解决它。

Here is my template: 这是我的模板:

public delegate T2 RestfulServiceRequest<T1, T2>(T1 req);

static class RestfulService
{
    public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req)
    {
        if (req == null)
           throw new BadRequestException(new ArgumentNullException("Invalid or missing request object"));

        try
        {
            return serviceCall(req);
        }
        catch (RestfulException e)
        {
            // log it and rethrow
            Logger.Write(e);
            throw;
        }
        catch (Exception e)
        {
            Logger.Error(e);

            // wrap in a consistent exception for propagation back to caller
            throw new InternalServerException(e);
        }
        finally
        {
            Logger.Debug("Complete");
        }
    }
}

} }

And here is a usage of it: 以下是它的用法:

public class Initialization : IInitialization
{
    // MyMethod thas uses the template
    public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req)
    {
        return RestfulService.ExceptionHandler<ApplianceInitReq, ApplianceInitResp>(delegate(ApplianceInitReq x)
        {
            // do some work
            return new ApplianceInitResp();
        }, req);
    }
}

} }

I would change 我会改变

public static T2 ExceptionHandler<T1, T2>(RestfulServiceRequest<T1, T2> serviceCall, T1 req)

to

public static T2 Invoke<T1, T2>( this T1 req, RestfulServiceRequest<T1, T2> serviceCall)

This will change the call to 这会将通话更改为

public class Initialization :IInitialization {
    public ApplianceInitResp CreateApplianceServer( ApplianceInitReq req ) {
        return req.Invoke( r => {
            // do some work
            return new ApplianceInitResp();
        });
    }
}

One thing that will make it cleaner is to define interfaces that your request/response objects implement. 使其更清晰的一件事是定义请求/响应对象实现的接口。 Then you can get rid of the generics in favor of the interfaces. 然后你就可以摆脱泛型,转而使用接口。 Note, too, the name change which I think is more descriptive of what you are really trying to do. 请注意,我认为名称更改更能描述您真正想要做的事情。

public interface IServiceResponse { ... }
public class ApplianceInitResp : IServiceResponse { ... }
public interface IServiceRequest { ... }
public class ApplianceInitReq : IServiceRequest { ... }

public delegate IServiceResponse RestfulServiceRequest( IServiceRequest req );

static class RestfulService
{
    public static IServiceResponse
        Invoke( RestfulServiceRequest serviceCall, IServiceRequest req)        
    {
        if (req == null)
            throw new BadRequestException( ...inner-exception... );
         try
         {
            return serviceCall(req);
         }
         catch (RestfulException e)
         {
            Logger.Write(e);
            throw;               
         }
         catch (Exception e)
         {
             Logger.Error(e);
             throw new InternalServerException(e);
         }
         finally
         {
             Logger.Debug("Complete");
         }
    }
}

public class Initialization : IInitialization
{
     // MyMethod thas uses the template 
     public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req) 
     {
          return RestfulService.Invoke(
                    delegate(ApplianceInitReq x)
                    {
                        // do some work
                        return new ApplianceInitResp();
                    },
                    req );
     }
}

I would suggest you to look for frameworks that offer AOP functionality (like Spring.NET, Unity). 我建议你寻找提供AOP功能的框架(如Spring.NET,Unity)。 Those will help you to reduce your CreateApplianceServer() call to mere 这些将帮助您减少CreateApplianceServer()调用

public ApplianceInitResp CreateApplianceServer(ApplianceInitReq req)
{
    // Do some work
    return new ApplianceInitResp();
}

by taking care of entrance/exit and exception logging with aspects. 通过处理入口/出口和异常日志记录与方面。 Probably, if you have some common parameters, you could plug the the argument validation into the aspect as well. 也许,如果你有一些常见的参数,你也可以将参数验证插入方面。

Of course, there would be a learning curve tax, but, trust me, result worth it. 当然,会有学习曲线税,但是,相信我,结果值得。 You will dramatically reduce amount of boilerplate code in your methods. 您将大大减少方法中的样板代码量。

I would agree that this feels a bit clunky to accomplish, however, I am not right away seeing the "pefect" way to re-work this to minimize duplication. 我同意这感觉有点笨拙,但是,我并没有立即看到重复工作的“完美”方式,以尽量减少重复。

THe only thing I'm thinking is that if you can somehow use interfaces or break it up a bit, I'm not sure exactly how I'd do it, but I can say that for on going changes I don't think I would leave this, at least not without really good documentation. 我唯一想到的是,如果你能以某种方式使用接口或稍微分解一下,我不确定我是怎么做的,但是我可以说,对于正在进行的改变我不认为我会离开这个,至少没有真正好的文档。

I think what you're doing is fine. 我觉得你做的很好。 This kind of coding is common in scripting languages -- but then it's less verbose because they're dynamically typed. 这种编码在脚本语言中很常见 - 但由于它们是动态类型的,所以它不那么冗长。 And it's also common in functional languages -- but then it's less verbose because they have such good type inference! 它在函数式语言中也很常见 - 但它的冗长程度更低,因为它们具有如此好的类型推断! In C# you're somewhat hampered by the current limitations of the type inference system, but that will get better in version 4. 在C#中,你受到类型推理系统当前局限性的限制,但在版本4中会更好。

The alternative is to use a proliferation of interfaces or classes to encapsulate your work. 另一种方法是使用大量的接口或类来封装您的工作。 As you do this, you have fewer type names at the point of invocation, but more code overall. 在执行此操作时,您在调用时具有较少的类型名称,但整体代码更多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM