3

在 C# 中,可以创建高阶函数,即。以函数g为参数的函数。假设我想创建这样一个函数,它给出一个函数f并返回另一个扩展其功能的函数。如何为返回的增强方法定义参数名称?动机是,我一般都在使用更高阶的方法,其中一些会产生新的方法。这些可能很难使用,因为它们没有附加参数名称等。

说明如何在 C# 中分别定义g和定义的示例:f

我定义了一个方法 Extend,它可以扩展以 aT作为参数并返回一个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 没有任何帮助,实际上我的论点是错误的。

如果我们可以定义 adelegate并将其转换为 this 那就太好了,但是,这是不可能的。

有什么建议么?

4

3 回答 3

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);
    }

顺便说一句,即使在 .net 4 之前,ConvertDelegate 函数也可用于获取委托的协方差。

于 2010-10-28T21:48:35.177 回答
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 中对逆变的特殊支持除外)。

于 2010-10-28T21:54:56.893 回答
0

这就是匿名代表的美妙之处;他们是匿名的。Func 是接受 int 并返回 int 的方法的委托。函数的实际作用以及参数的名称是无关紧要的。

唯一可行的方法是,如果 Calc 是一个命名的委托类型,使用与 CalcPriv 相同的签名定义。包括匿名扩展在内,它仍然可以正常工作,但是您将拥有一个用于 Calc 的命名参数。

传递信息的另一种方法是向 xml-doc Calc 提供一个标签,该///<summary></summary>标签描述了 Calc 旨在采用的参数。

最后,您可以派生自Func<T,TResult>创建一个TakesPositiveInteger<T,TResult>类。这有点远,但如果你想谈谈自记录代码......

于 2010-10-28T21:25:22.577 回答