8

我正在使用一个函数,该函数将两个函数作为参数,并返回一个新的组合函数:

public static Action<T> Compose<T>(Action<T> first, Action second)
{
    return new Action<T>(arg =>
    {
        first(arg);
        second();
    });
}

我注意到,如果我没有指定T,编译器会在向它发送静态或成员函数(而不是实际Action<T>对象)时抱怨:

static void Main(string[] args)
{
    // compiler error here
    var composed = Compose(Test, () => Console.WriteLine(" world"));
    composed("hello");

    Console.ReadLine();
}

public static void Test(string arg)
{
    Console.Write(arg);
}

错误信息:

无法从用法中推断出方法“ConsoleTest.Program.Compose(System.Action, System.Action)”的参数。尝试明确指定类型参数。

我的问题:为什么不能在这里推断类型参数?的签名Test在编译时是已知的,不是吗?真的有一些功能可以代替Test,这会导致其签名不明确吗?

脚注:我知道我可以简单地发送new Action<string>(Test)而不是发送TestCompose(如本问题所述)——我的问题是“为什么”,而不是“我该怎么做”。

4

2 回答 2

7

我想这可能与这样一个事实有关,至少从编译器的角度来看,Test它实际上是一个“方法组”,直到编译器确定了它将拥有的参数类型。即使组中只有一种Test方法(当前范围内只有一种方法)也是如此。

观察:

var composed = Compose<object>(Test, () => Console.WriteLine(" world"));

产生错误:

' ' 的最佳重载方法匹配Compose<object>(System.Action<object>, System.Action)有一些无效参数

参数 1:无法从“方法组”转换为“ System.Action<object>

但这很好:

var composed = Compose<string>(Test, () => Console.WriteLine(" world"));

我的猜测是,编译器在某种意义上将方法组表达式 ( Test) 和隐式类型的泛型方法调用 ( Compose) 视为“未绑定”。从参数的类型“未绑定”签名中无法完全确定从方法组中选择哪个方法,也无法从签名中Compose确定选择哪个类型类型参数Compose。为了编译整个语句,它需要一个或另一个被“绑定”。

于 2013-09-25T21:27:56.053 回答
0

这可能与协方差有关。尽管参数的类型Test是已知的,但您可能希望创建更具体类型的委托。

public class BaseClass { }
public class DerivedClass : BaseClass { }

static class Program
{
    static void Main(string[] args)
    {
        var composed = Compose<DerivedClass>(Test, () => Console.WriteLine(" world"));
        composed(new DerivedClass());

        Console.ReadLine();
    }

    public static void Test(BaseClass arg)
    {
        Console.Write(arg);
    }
}
于 2013-09-25T21:33:08.213 回答