2010-10-28 67 views
3

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

示出了如何gf和分别可在C#中定义的一个例子:

我定义的方法扩展可以EXTENS方法采取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并将其转换为此值将会很好,但这是不可能的。

有什么建议吗?

+1

你需要知道参数名称?您在何处/如何在您发布的代码中使用它? – sepp2k 2010-10-28 21:15:03

+0

有没有这样做是不同的,只是创建一个继承M和重写Extend的类? – 2010-10-28 21:23:19

+0

以上是一个例子,而不是问题。问题是关于能够使用记录参数输入的更高阶方法创建方法 – 2010-10-28 21:25:56

回答

2

如果你只是想用命名的参数固定的委托类型,然后你可以定义自己的委托类型:

Func键仅仅是这样定义的:

public delegate TResult Func<in T, out TResult>(T arg) 

所以,你可以定义你想要的参数名称自己的委托类型。

但在你的例子中,你想保留传入的委托类型,所以这在这里不起作用。从理论上讲,你可以定义你的函数是这样的:

public static T Extend(T functionToWrap) 
{ 
} 

不幸的是,制约与正确的签名(或甚至只是代表的话)的输入型的代表没有很好的通用约束。但是如果没有这些限制,实施会变得如此丑陋,而且你会失去太多的静态类型安全,以至于IMO不值得。

一种解决方法是使用:

new MyFunc(Extend(f)) 

其中MYFUNC定义你想要的parameternames。

或者你可以做到以下几点:

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

+0

+1,我没有意识到你可以像这样构造一个参数匹配的委托。 – 2010-10-28 22:10:19

+0

我更喜欢调用者构造所需类型的新委托的解决方法。它仍然很短,易于阅读和类型安全。 – CodesInChaos 2010-10-28 22:18:35

0

这就是匿名代表的美丽;他们是匿名的。 Func是一个接受int并返回int的方法的委托。函数实际做了什么,因此参数的名称是无关紧要的。

这将工作的唯一方法是如果Calc是一个命名的委托类型,定义与签名相同的CalcPriv。它仍然可以按照书面形式工作,包括匿名扩展,但是您将拥有Calc的命名参数。

传递信息的另一种方式是使用描述Calc参数的///<summary></summary>标签的xml-doc Calc。

最后,你可以从Func<T,TResult>派生出来创建一个TakesPositiveInteger<T,TResult>类。这是走的太远了一点,但如果你想谈谈自记录代码...

+0

将代码更改为'public delegate int Calc(int someArg); public Calc CalcM; public Calc2() { CalcM =(Calc)M.Extend (CalcPriv); } '不起作用 – 2010-10-28 21:29:02

1

有可能转换为用命名参数委托,通过一个新构造的代表动态结合到下面的函数功能委托方法:

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中的变换)。

+0

哇,太好了,也许Extend方法也应该把委托类型作为参数,所以它可能是执行Delegate.CreateDelegate(typeof(...))的那个) – 2010-10-28 21:59:26

+0

如果extended是一个多播委托,这是否正确工作?但是由于Extend对多播委托无用,所以在这种情况下check +异常应该足够了。如果所需的委托类型在创建代码时已知,那么您可以简单地使用新的CalFunc(扩展)。 – CodesInChaos 2010-10-28 22:09:24

+0

@Carlo,请参阅CodeInChaos的解决方案;委托构造函数更干净,更安全。 – 2010-10-28 22:11:02