无论参数数量多少,都可以重用逻辑的模式

时间:2012-11-05 15:12:09

标签: c# logging

我想在几种方法之前和之后调用一些记录逻辑。每种方法都接受不同数量/类型的参数。我正在尝试设置它,所以当我调用每个方法时,我不必复制日志记录逻辑。我已经能够通过创建一些代表来减少重复的数量。我为每个使用的parms数量/类型创建了一个委托,我有一个接受每个委托并进行日志记录的方法。但是,我仍然有大约6个不同的代表,因此这六个逻辑重复。

我认为无论parms的数量如何,都可以修改它,我有一种方法可以进行日志记录并调用方法。但我无法弄明白。

以下是其中一个代表和我试图不重复的逻辑示例。

    public delegate void LineOfBusinessHandler(DateTime runDate, LineOfBusinessCode lineOfBusinessCode);

    public void Run(DateTime runDate, ProcessCode process, LineOfBusinessCode lineOfBusinessCode, LineOfBusinessHandler del)
    {
        this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Started.ToString(), null, runDate);


        try
        {
            del(runDate, lineOfBusinessCode);
            this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Finished.ToString(), null, runDate);
        }
        catch (Exception e)
        {
            int errorId = SystemManager.LogError(e, process.ToString());
            this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Errored.ToString(), errorId, runDate);
        }
    }

4 个答案:

答案 0 :(得分:1)

我意识到这可能超出了你所寻找的范围和/或能力。但是如果你有一个通用的日志记录逻辑,你希望在不同的方法调用上重用,而不会丢失类型安全(即不在object[]中传递你的参数),那么走的方法是拦截。你需要一个框架(我不建议你自己编写自己的框架!),它可以提供AOP,依赖注入或类似的东西。这些事情通常可以处理拦截。

例如,我有一个与Ninject一起使用的日志拦截器:

public void Intercept(IInvocation invocation)
{
    var logger = LoggerFactory.GetLogger(invocation.Request.Method.DeclaringType);
    var debug = !invocation.Request.Method.IsSpecialName && logger.IsDebugEnabled;
    if (debug)
        logger.Debug(invocation.Request.Method.Name);
    try
    {
        invocation.Proceed();
        if (debug)
            logger.Debug(invocation.Request.Method.Name + " FINISH");
    }
    catch (Exception)
    {
        logger.Error(invocation.Request.Method.Name + " ERROR");
        throw;
    }

}

然后我通过使用Ninject获取它们来创建我的对象(如果您不知道它,请查看一些tutorials),同时向它们添加一些拦截,例如:Kernel.Bind<MyTypeToLog>().ToSelf().Intercept().With<LoggingInterceptor>();其中{ {1}}使用上面显示的方法实现LoggingInterceptor ...

如果您需要更多细节帮助,请说出来!

编辑:刚才意识到我的示例没有显示这个,但你也可以访问调用的参数(作为对象集合)!!

答案 1 :(得分:0)

如果我正确理解您的问题,听起来您可以使用C# params 关键字。有关如何使用它的参考,请参阅此内容:http://msdn.microsoft.com/en-us/library/w5zay9db.aspx

使用 params 时的一个要求是它必须放在函数签名的最后。然后,在函数内部,您可以枚举并遍历变量参数列表,就像它是一个数组一样。

修改

要扩展@Ben Voigt发布的评论,使用 params 关键字的另一个限制是它要求变量参数列表属于同一类型。然而,这可以在您的情况下减轻,因为您关心的只是记录。在这种情况下,您可能会在需要记录的对象上调用ToString()方法,因此您可以创建类型为object的变量参数列表。

如果调用ToString()是不够的,并且您有不同类型的对象,则可以使所有这些对象实现公共接口。我们称之为ILoggableObject,它公开了一种提供日志输出的方法。如果你有能力改变这些对象的话。

答案 2 :(得分:0)

C#语言没有任何元编程语法。你必须使用反射。您当然可以反映任意方法/委托来确定参数类型,然后构建一个记录参数并调用原始方法的方法,编译这个新的包装器方法,并返回一个与原始方法具有相同调用签名的委托。

您可以在运行时执行此操作(返回委托)或使用所有包装函数构建新程序集,然后由代码引用并正常使用。

您应该查看用于面向方面编程的代码编织工具。其中一些人已经这样做了。

与使用params数组不同,这为您提供了一个与原始方法具有相同签名(或委托类型)的包装器,因此它是类型安全的并且Intellisense可以工作(与任何其他委托一样多。)

答案 3 :(得分:0)

这取决于不同版本之间的共同点,但假设runDate和process很常见,你可以这样做:

public void Run(DateTime runDate, ProcessCode process, LineOfBusinessCode lineOfBusinessCode, LineOfBusinessHandler del)
{
    this.DoRun(runDate, process, (d, p) => del(d, p, lineOfBusinessCode));
}

public void DoRun(DateTime runDate, ProcessCode process, Action<DateTime, ProcessCode> action)
{
    this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Started.ToString(), null, runDate);


    try
    {
        action(runDate, process);
        this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Finished.ToString(), null, runDate);
    }
    catch (Exception e)
    {
        int errorId = SystemManager.LogError(e, process.ToString());
        this.ProcessManager.AddToBatchLog(process.ToString(), ProcessStatus.Errored.ToString(), errorId, runDate);
    }
}

您甚至可以进行概括,因此您无需像这样定义自定义委托:

public void Run<T1>(DateTime runDate, ProcessCode process, T1 param1, Action<DateTime, ProcessCode, T1> del)
{
    this.DoRun(runDate, process, (d, p) => del(d, p, param1));
}

public void Run<T1, T2>(DateTime runDate, ProcessCode process, T1 param1, T2 param2, Action<DateTime, ProcessCode, T1, T2> del)
{
    this.DoRun(runDate, process, (d, p) => del(d, p, param1, param2));
}