我正在使用一个函数,该函数将两个函数作为参数,并返回一个新的组合函数:
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)
代替Test
发送给Compose
(如this question所述) - 我的问题是“为什么” ,而不是“我怎么能这样做”。
答案 0 :(得分:6)
我认为它可能与以下事实有关:至少从编译器的角度来看,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
的{{1}}的类型类型参数签名。它需要一个或另一个'绑定'才能编译整个语句。
答案 1 :(得分: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);
}
}