你能写一个可以接受任意数量参数的c#decorator函数吗
本文关键字:任意数 参数 函数 c#decorator 一个 | 更新日期: 2023-09-27 18:28:27
我目前有一段代码,它的行为很像python装饰器,它将一个函数作为参数,并返回由另一个函数包装的相同函数(在本例中,打开和关闭性能连接)。
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[]。
您可以只使用Func<T>
(而不是Func<TArg,TResult>
),并允许编译器通过lambda表达式中的闭包为您处理多个参数。
如果您将方法更改为:
public Func<T> EnableP4<T>(Func<T> function)
您可以随时通过以下方式进行调用:
var newFunc = EnableP4(() => SomeFunc(arg1, arg2, arg3));
这很好,因为它允许任何数量的参数,而没有多个重载。
不,您可以在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)允许您将所有方法封装在一个接口/类中,因此在某些情况下它可能会起作用(可能不是这种情况,因为它看起来像是在数据访问层内对代码进行某种重构)
您不需要传入所有参数;相反,您可以传递一个已经提供了参数的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;
}
C#中不支持可变数量的类型参数,因此我认为不可能用当前的参数来实现您想要的功能。.Net框架本身有这么多func/action委托声明是有原因的。这在同一名称空间中的元组中也可见。
我认为您可以使用参数对象来解决这个问题,它基本上是一个封装所有参数的类。如果你为这些模型创建一个基类或接口,你可以限制你的decorator方法:
public Func<TArgs, TReturn> EnableP4<TReturn, TArgs>(Func<TArgs, TReturn> function)
where TArgs : IArgs
这与框架内事件中使用的概念非常相似,其中传递EventArgs参数对象。这里的想法是一样的,您必须继承基本模型/接口,并使用所需的参数创建特定的模型。
当然,这不是最优的,因为它需要大量修改,而且对于简单的方法调用来说是非标准的,但我认为它应该可以很好地工作。
你考虑过方法拦截吗?例如,我知道Unity支持它。