使用自定义属性装饰方法

4

我有一些函数需要记录它们所消耗的时间。

DBResult LongTask(DBCommand command)
{
   ...
}

有没有方法可以实现这个?
[LogTimeUsed()]
DBResult LongTask(DBCommand command)
{
   ...
}

我希望有一个类/函数,每次调用该函数时都会被调用,并且我可以注入自己的代码,获得对命令和DBResult的访问并记录函数执行所花费的时间。

6个回答

4

在.NET中没有内置的方法可以实现这个功能。

如果您不想使用任何第三方库(正如您在其中一个评论中所说),则进行代码编织或动态代理生成是一项很大的工作。在这种情况下,最好放弃使用属性并采用装饰器设计模式。这里有一个例子:

// Define an interface for the operation
public interface IMyLongRunningTask
{
    DBResult longTask(DBCommand command);
}

// Define an implementation for the operation:
public class MyLongRunningTask : IMyLongRunningTask
{
    public DBResult longTask(DBCommand command)
    {
        // code here
    }
}

现在你可以为IMyLongRunningTask编写一个装饰器:

public class MyLongRunningTaskMonitor : IMyLongRunningTask
{
    private readonly IMyLongRunningTask wrappedService;
    private readonly ILogger logger;

    public MyLongRunningTaskMonitor(IMyLongRunningTask wrapped,
        ILogger logger)
    {
        this.wrappedService = wrapped;
        this.logger = logger;
    }

    public DBResult longTask(DBCommand command)
    {
        var watch = Stopwatch.CreateNew();

        var result = this.wrappedService.longTask(command);

        this.logger.Log("longTask executed in " + 
            watch.ElapsedMilliseconds + " ms.");

        return result;
    }
}

当您使用依赖注入时,您可以轻松地配置一个MyLongRunningTaskMonitor以在请求IMyLongRunningTask时返回。例如:
container.Register<IMyLongRunningTask>(() =>
    new MyLongRunningTaskMonitor(
        container.GetInstance<MyLongRunningTask>(),
        container.GetInstance<ILogger>()
    )
);

哦。但是这种方法迫使我在创建类实例时使用“特定”的语法。 - 0xDEAD BEEF
1
使用依赖注入模式会强制你朝着特定的方向前进(因为这就是模式的作用)。DI/IoC框架都有自己的“语言”来配置这种依赖关系。当你正确地使用DI模式时,你可以让应用程序中除了启动代码之外的所有代码都不涉及到这种依赖关系的连接。 - Steven

2
你可以使用Castle DynamicProxy
DynamicProxy为你的对象生成代理,你可以使用它们来透明地添加或更改行为,提供前/后处理以及许多其他功能。

我该如何自己实现这个功能?我不想使用第三方类/软件。我该如何创建这个装饰器?这个东西叫做代理吗?我该如何编写自己的代理? - 0xDEAD BEEF
3
你为什么想要亲自写这个呢?重复造轮子没有多大意义。你可以获取Castle源代码并查看他们是如何实现它的 - 这是开源的。 - Jakub Konecki

0

0
问题是寻找一种使用C#属性创建装饰器的方法,以尝试创建Python中看到的装饰器语法(这非常好... C#社区请注意)。
通过将“属性”嵌入您的方法名称中(例如LongTask_LogTimeUsed),然后在LongTask方法内部有少量(样板)代码读取其方法名称,将前处理或后处理委派给适当的处理程序,可以实现类似的功能:
如何从代码中获取当前方法的名称
不过,需要考虑如何将传递的参数传递到前处理或后处理器中。

对于C#开发人员来说,装饰方法名称是不可行的。装饰的整个想法是使其对外部不可见。C#开发人员将创建代理类并包装调用。但是,使用属性来实现这一点是一个好主意。看起来除了使用代码生成器和依赖注入来完成这个任务之外,没有其他选择了。 - 0xDEAD BEEF

0
如果你自己没有处理特定类型的属性,你应该研究一下面向切面编程,使用像PostSharp这样的工具。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接