在C#中,可以创建更高阶的函数,即。函数g
将函数作为参数。假设我想创建一个给定函数f
并返回另一个扩展其功能的函数的函数。 如何为返回的增强方法定义参数名称?我的动机是,我正在使用更高阶的方法,其中一些产生新的方法......这些很难使用因为没有附加参数名称等。
说明如何在C#中分别定义g
和f
的示例:
我定义了一个方法Extend,它可以扩展以T
为参数并返回S
的方法。
static class M
{
public static Func< T, S> Extend(Func< T, S> functionToWrap)
{
return (someT) =>
{
...
var result = functionToWrap(someT);
...
return result;
};
}
}
然后我们可以在我们的类上扩展一个方法而不改变方法。
class Calc2
{
public Func< int, int> Calc;
public Calc2()
{
Calc = M.Extend< int, int>(CalcPriv);
}
private int CalcPriv(int positiveNumber)
{
if(positiveNumber < 0) throw new Exception(...);
Console.WriteLine("calc " + i);
return i++;
}
}
唉,参数名称positiveNumber
不再可用,因为唯一可用的信息是Func<int, int> Calc
。那是当我通过键入new Calc2().Calc(-1)
来使用扩展方法时,我得不到IDE的帮助,实际上我的论证是错误的。
如果我们可以定义delegate
并将其投射到此,那将是很好的,但是,这是不可能的。
有什么建议吗?
答案 0 :(得分:2)
如果您只想要一个带有命名参数的固定委托类型,那么您只需定义自己的委托类型:
Func就是这样定义的:
public delegate TResult Func<in T, out TResult>(T arg)
因此,您可以使用所需的参数名称定义自己的委托类型。
但是在您的示例中,您希望保留传入的委托类型,因此这在此处不起作用。从理论上讲,你可以像这样定义你的函数:
public static T Extend(T functionToWrap)
{
}
不幸的是,没有好的通用约束将输入类型限制为具有正确签名的委托(或者甚至只是委托)。但是如果没有这些限制,实施就会变得如此丑陋,你会失去如此多的静态类型安全,而IMO则不值得。
一种解决方法是使用:
new MyFunc(Extend(f))
其中MyFunc定义了您想要的参数名称。
或者您可以执行以下操作:
public static T ConvertDelegate<T>(Delegate d)
{
if (!(typeof(T).IsSubclassOf(typeof(Delegate))))
throw new ArgumentException("T is no Delegate");
if (d == null)
throw new ArgumentNullException();
MulticastDelegate md = d as MulticastDelegate;
Delegate[] invList = null;
int invCount = 1;
if (md != null)
invList = md.GetInvocationList();
if (invList != null)
invCount = invList.Length;
if (invCount == 1)
{
return (T)(object)Delegate.CreateDelegate(typeof(T), d.Target, d.Method);
}
else
{
for (int i = 0; i < invList.Length; i++)
{
invList[i] = (Delegate)(object)ConvertDelegate<T>(invList[i]);
}
return (T)(object)MulticastDelegate.Combine(invList);
}
}
public static TDelegate Extend<TDelegate,TArg,TResult>(Func<TArg,TResult> functionToWrap)
where TDelegate:class
{
Func<TArg,TResult> wrappedFunc= DoTheWrapping(functionToWrap);
return ConvertDelegate<TDelegate>(wrappedFunc);
}
BTW ConvertDelegate函数可用于在.net 4之前获得代表的Co / Contravariance。
答案 1 :(得分:1)
通过将新构造的委托动态绑定到基础Func委托方法,可以强制转换为具有命名参数的委托:
public delegate double CalcFunc(double value);
static class M
{
public static Func<T, S> Extend<T,S>(Func<T, S> functionToWrap)
{
return (someT) => functionToWrap(someT);
}
}
class Program
{
private static double Calc(double input)
{
return 2*input;
}
[STAThread]
static void Main()
{
Func<double, double> extended = M.Extend<double, double>(Calc);
CalcFunc casted = (CalcFunc)Delegate.CreateDelegate(typeof(CalcFunc), extended.Target, extended.Method);
Console.WriteLine(casted(2) + " == 4");
Console.WriteLine("I didn't crash!");
Console.ReadKey();
}
}
一句警告:这不会对演员表进行任何编译时检查。如果签名不完全匹配,您将在运行时遇到绑定失败(除了对.NET 4中的逆变的特殊支持)。
答案 2 :(得分:0)
这就是匿名代表的美丽;他们是匿名的。 Func是一个接受int并返回int的方法的委托。该函数实际上做了什么,因此参数的名称是无关紧要的。
唯一可行的方法是,如果Calc是一个命名委托类型,使用与CalcPriv相同的签名定义。它仍然可以按照书面形式工作,包括匿名扩展,但是你有一个Calc的命名参数。
传递信息的另一种方法是使用///<summary></summary>
标记来xml-doc Calc描述Calc设计要采用的参数。
最后,您可以从Func<T,TResult>
派生以创建TakesPositiveInteger<T,TResult>
课程。这有点远,但如果你想谈论自我记录代码......