C#君:没有源码,但想加点逻辑,该怎么办?

96 阅读2分钟

哈喽,大家好,我是了不起。

故事

最近有个搞C#的新同事加班时抱怨上级给了一个这样无理的需求:

原本代码是这样的:

var code = teamManager.GetCurrentUserCode();
#code 输出:teamA_123

需要达到类似这样的效果:

var newCode = teamManager.GetCurrentUserCode();
#newCode 输出:companyA_teamA_123

注意,这里的teamManager为第三方封装好的接口对象,得不到它的对象源码,无法修改里面的方法。

这位新同事懵懂地如下修改了所有自己调用过“GetCurrentUserCode”的地方:

var companyCode = companyService.GetCurrentCompanyCode();
var newCode = companyCode+"_"+teamManager.GetCurrentUserCode();
#newCode 输出:companyA_teamA_123

看起来很简单,但很繁琐,且不太干净(Clean)。

“哈哈,改完收工!...咦?怎么这个地方还是输出原来的结果?...”

原来还有别处看不见源代码的组件也同样调用了“GetCurrentUserCode”,按需求来说,所有这些地方必须要改。

“但没有源码啊,怎么改?!”他大骂XXX的无理需求。

解决方案

兄弟,别急别急,其实只要不是做得太差的第三方组件,都使用了IOC容器技术,比如Autofac。

现在我们来看看当前较流行的一种优雅解决方法:Autofac + Castle DynamicProxy。

以下示例基于.NET5 版本演示:

1)安装所需要的包:

dotnet add package Autofac.Extras.DynamicProxy

2)引用 Castle.DynamicProxy,定义一个 AddCompanyNameInterceptor 拦截器:

...
using Castle.DynamicProxy;
...
public class AddCompanyNameInterceptor : IInterceptor
{

    #我方定义的接口
    private readonly ICompanyService companyService;
    
    public AddCompanyNameInterceptor(ICompanyService companyService)
    {
        this.companyService=companyService;
    }

    public void Intercept(IInvocation invocation)
    {
        #原本的对象执行方法
        invocation.Proceed()
        
        if(invocation.Method.Name!="GetCurrentUserCode")return;
        
        var oldValueCode=invocation.ReturnValue.ToString();
        #oldValueCode 输出:teamA_123
        
        var companyCode = companyService.GetCurrentCompanyCode();
        #companyCode 输出:companyA
        
        var newCode=companyCode+"_"+oldValueCode;
         #newCode 输出:companyA_teamA_123
         
         #重新设置返回的值
         invocation.ReturnValue=newCode;
    }
}

3)使用刚刚创建的“AddCompanyNameInterceptor”进行注册拦截:

var autofacBuilder = new ContainerBuilder();

#注册AddCompanyNameInterceptor
autofacBuilder.RegisterType<AddCompanyNameInterceptor>().AsSelf();

#注册拦截 ITeamManager 服务
autofacBuilder.RegisterType<ITeamManager>()
    .AsImplementedInterfaces()
    .EnableInterfaceInterceptors()
    .InterceptedBy(typeof(AddCompanyNameInterceptor));

4)之后就可以这样使用:

var teamManager = autofacContainer.Resolve<ITeamManager>();
var newCode = teamManager.GetCurrentUserCode();
#code 输出:companyA_teamA_123

聊聊

Castle DynamicProxy 作为一个开源的.NET库,其实它背后的思想就是 AOP(Aspect-Oriented Programming,面向切面的编程),意在通过预编译方式和运行期动态代理来实现在不修改源代码的情况下给系统对象动态添加功能的一种技术,属于传统OOP编程的一种补充。AOP广泛地应用日志记录,权限验证,异常拦截等。

当然,以上只是解决方案的一种,其他小伙伴如果有其他方法也可以在评论区留言,帮帮这位新同事。

关注公众号[程序员了不起]回复[eee113] 即可获取navicat破解版。