【问题标题】:Static method signature type arguments and partial application静态方法签名类型参数和部分应用
【发布时间】:2018-11-11 08:34:52
【问题描述】:

我最近一直在研究函数式编程,并希望将一些概念带入我的C# 世界。我正在尝试编写函数来创建服务(或任何你称之为的服务),而不是创建具有可注入依赖项的类。

我想出了一种方法,通过创建这样的静态方法来部分应用具有两个参数和一个返回参数的函数(与注入依赖项具有相同的效果):

// this makes a func with a single arg from a func with two
static Func<T2, TResult> PartiallyApply<T1, T2, TResult>(
        Func<T1,T2, TResult> f, 
        T1 t1)
    {
        // use given t1 argument to create a new function
        Func<T2, TResult> map = t2 => f(t1, t2);
        return map;
    }

这行得通,但是我想给它传递一个静态方法,比如这个:

static string MakeName(string a, string b) => a + " " + b;

当我尝试将其连接起来时,我收到错误 The type arguments for method 'Program.PartiallyApply&lt;T1, T2, TResult&gt;(Func&lt;T1, T2, TResult&gt;, T1)' cannot be inferred from the usage. 但是当我添加一个创建显式 Func&lt;string,string,string 的步骤时,我指出它确实有效的方法:

static void Main(string[] args)
{
    var first = "John";
    var last  = "Doe";
    var f1    = PartiallyApply(MakeName, first);   // cannot be inferred from the usage

    Func<string, string, string> make = MakeName;  // map it to func            
    var f2 = PartiallyApply(make, first);          // works
    var name = f2(last);

    Console.WriteLine(name);
    Console.ReadKey();
}

为什么直接传递静态方法时编译器无法计算出类型args?有没有一种方法可以使用静态方法,而无需显式地将它们映射到具有基本相同(类型)参数的Func&lt;&gt;

更新 阅读Enrico BuonannoFunctional programming in C#(强烈推荐)为解决这个问题提供了另一个不错的选择。在7.1.3 中,他提供了几个关于如何直接使用Funcs 而不是方法组的选项。 您可以使用 Func 像这样创建一个 getter only 属性:

static Func<string, string, string> MakeName => (a,b) => a + " " + b;

【问题讨论】:

标签: c# functional-programming func partial-application


【解决方案1】:

因为如果你有两个方法不同的参数编译器不知道使用方法1或方法2。

示例:

static string MakeName(string a, string b) => a + " " + b;
static string MakeName(int a, string b) => a + " " + b;

编译器怎么知道你指的是哪一个?方法1还是方法2?仅仅因为您现在在方法组中只有一种方法,并不意味着它将始终如此。然后添加一个方法会以这种方式中断。

var f1 = PartiallyApply(MakeName, first);

所以如果你想解决这个问题,你必须在方法调用中设置你的泛型参数:

var f1 = PartiallyApply<string, string, string>(MakeName, first);
var f2 = PartiallyApply<string, int, string>(MakeName, first);

或者您可以在 PartiallyApply 方法中获取所有参数:

static string MakeName(string a, string b) => a + " " + b;
    static string MakeName(int a, string b) => a + " " + b;
    // this makes a func with a single arg from a func with two
    static Func<T2, TResult> PartiallyApply<T1, T2, TResult>(
            Func<T1, T2, TResult> f,
            T1 t1,
            T2 t2)

    {
        // use given t1 argument to create a new function
        Func<T2, TResult> map = result => f(t1, t2);
        return map;
    }



    static void Main(string[] args)
    {
        var first = "John";
        var last = "Doe";
        var f1 = PartiallyApply(MakeName, first, last);   //works now
        var name = f1(last);
        Console.WriteLine(name);

        Func<string, string, string> make = MakeName;  // map it to func            
        var f2 = PartiallyApply(make, first, last);          // works
        name = f2(last);

        Console.WriteLine(name);
        Console.ReadKey();
    }

【讨论】:

  • 当您有一个带有重载的方法(例如您的示例)时,我知道编译器由于模棱两可而无法弄清楚。然而,在我的例子中,只有一个候选人。签名为string,string -&gt; string的方法。
  • @cr4ne to quote Eric Lippert's comment in the proposed dupe, '说“方法组只包含一个方法,所以让我们暂时说即使我们不知道参数,重载解析也会成功”本质上是添加一个新的, 奇怪的重载解析算法,仅在包含一个方法的方法组的极少数情况下成功。 ...您真的想要添加第二个重载会严重改变类型推断的工作方式的情况吗?'
  • @cr4ne 编译器必须关心您在期货中添加的方法
猜你喜欢
  • 2020-02-15
  • 1970-01-01
  • 1970-01-01
  • 2014-01-06
  • 2010-09-16
  • 1970-01-01
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多