你能写一个可以接受任意数量参数的 c# 装饰器函数吗?

Posted

技术标签:

【中文标题】你能写一个可以接受任意数量参数的 c# 装饰器函数吗?【英文标题】:Can you write a c# decorator function that can take any number of arguments? 【发布时间】:2013-08-24 04:10:32 【问题描述】:

我目前的代码很像 python 装饰器,它接受一个函数作为参数并返回由另一个包装的相同函数(在这种情况下打开和关闭 perforce 连接)。

    public Func<TArg, TReturn> EnableP4<TReturn, TArgs>(Func<TArg, TReturn> function)
    
        Func<TArg, TReturn> p4Wrapper = (TArg funcArg) =>
        
            try
            
                if (con.Status.Equals(ConnectionStatus.Disconnected))  con.Connect(options); 
                return function(funcArg);
            
            finally  con.Disconnect(); 
        ;
        return p4Wrapper;
    

目前这仅适用于具有一个参数的函数,我想知道它是否可以更通用(也许有一种方法可以将数组解包到方法中?)。

(类似这样的东西?)

    public Func<TArgs, TReturn> EnableP4<TReturn, TArgs>(Func<TArgs, TReturn> function)
    
        Func<TArgs, TReturn> p4Wrapper = (TArgs args) =>
        
            try
            
                if (con.Status.Equals(ConnectionStatus.Disconnected))  con.Connect(options); 
                return function(*args);
            
            finally  con.Disconnect(); 
        ;
        return p4Wrapper;
    

其中 TArgs 是一个 TArg[]。

【问题讨论】:

你能举例说明你打算如何使用它吗?我不太确定我是否了解您要查找的内容。 【参考方案1】:

您可以只使用 Func&lt;T&gt;(而不是 Func&lt;TArg,TResult&gt;),并允许编译器通过 lambda 表达式中的闭包为您处理多个参数。

如果您将方法更改为:

public Func<T> EnableP4<T>(Func<T> function)

您总是可以通过以下方式调用它:

var newFunc = EnableP4(() => SomeFunc(arg1, arg2, arg3));

这很好,因为它允许任意数量的参数而没有多个重载。

【讨论】:

【参考方案2】:

不,您完全可以在 C# 中使用强类型结果执行此操作。这主要是由于返回类型 - 您可以相对容易地使用任意数量的参数(即 Emit 或反射)构造函数调用,但要获得强类型结果,您必须拥有多个函数。

通常的方法是为 0-n(通常 0-3 就足够了)数量的参数中的每一个设置多个函数。

public Func<TReturn> EnableP4<TReturn>(Func<T1, TReturn> function)...
public Func<T1, TReturn> EnableP4<TReturn, T1>(Func<T1, TReturn> function)....
public Func<T1, T2, TReturn> 
        EnableP4<TReturn, T1, T2>(Func<T1, T2, TReturn> function)...

注意事项:

你可以试试dynamic,看看你是否接近你想要的 还有依赖注入框架(如 Unity)允许您将所有方法包装在一个接口/类中,因此在某些情况下它可能会起作用(可能不是这种情况,因为它看起来是用于在数据访问层)。

【讨论】:

【参考方案3】:

你不需要传入所有的参数;相反,您可以传递一个已经提供了参数的 lambda。

这很容易通过一个例子来解释。鉴于此:

public Func<T> Wrapper<T>(Func<T> function)

    Func<T> wrapper = () =>
    
        try
        
            Console.WriteLine("Prequel");
            return function();
        

        finally
        
            Console.WriteLine("Sequel");
        
    ;

    return wrapper;

你可以这样做:

    void run()
    
        // Supply arguments to test() here:
        var wrapped = Wrapper(() => test(1, 2));
        Console.WriteLine(wrapped());
    

    private int test(int a, int b)
    
        Console.WriteLine("test()");
        return a + b;
    

【讨论】:

【参考方案4】:

类型参数的可变数量是unsupported in C#,所以我认为用现有的东西做你想做的事是不可能的。 .Net 框架本身有that many func/action delegate declarations 是有原因的。这对于同一个命名空间上的元组也是可见的。

我认为您可以使用参数对象来解决它,它基本上是一个封装所有参数的类。如果您随后为这些模型创建基类或接口,则可以限制您的装饰器方法:

public Func<TArgs, TReturn> EnableP4<TReturn, TArgs>(Func<TArgs, TReturn> function)
    where TArgs : IArgs

这与框架内事件中使用的概念非常相似,其中传递了一个 EventArgs 参数对象。这里的想法是相同的,您必须继承基本模型/接口并使用您想要的参数创建特定模型。

这当然不是最优的,因为它需要大量修改并且对于简单的方法调用来说是非标准的,但我认为它应该可以很好地工作。

您是否考虑过为此进行方法拦截?例如,我知道Unity supports it。

【讨论】:

以上是关于你能写一个可以接受任意数量参数的 c# 装饰器函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

python之decorator 装饰器

python之decorator 装饰器

python进阶之装饰器之2.定义一个可接受参数的装饰器如何定义一个属性可由用户修改的装饰器定义一个能接受可选参数的装饰器

Python学习第十一课——装饰器

Python装饰器

Python 构造一个可接受任意数量参数的函数