为什么C#编译器不能从函数签名推断泛型委托类型?

8

我正在处理一个接受两个函数作为参数并返回一个新组合函数的函数:

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

我注意到,如果我将静态或成员函数(而不是实际的 Action<T> 对象)发送给编译器时不指定 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,使其签名模糊不清吗? 注:我知道我可以简单地将Test替换为new Action<string>(Test)发送到Compose(如此问题中所述)——我的问题是“为什么”,而不是“我该怎么做”。

1
FYI -- Compose<string> 也可以解决这个问题。 - Austin Salonen
3
请看这个回答:https://dev59.com/p2025IYBdhLWcg3wFxqa - lukegravitt
感谢@lukegravitt--那里的最佳答案引用了语言规范,并且Lippert本人也发表了意见。 - McGarnagle
2个回答

8

我猜这可能与编译器的角度有关,至少从编译器的角度来看,Test 实际上是一个“方法组”,直到编译器确定它将拥有哪些参数类型为止。即使组中只有一个方法(当前作用域中只有一个Test 方法),这也是真实的。

观察:

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

产生了错误:
引用: “Compose (System.Action ,System.Action)”的最佳重载方法具有一些无效的参数 参数1:无法从“方法组”转换为“System.Action ”
但这个是正常的:
var composed = Compose<string>(Test, () => Console.WriteLine(" world"));

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

2
看起来你走在了正确的轨道上...这个问题还有更多相关的讨论,可以参考这里:https://dev59.com/p2025IYBdhLWcg3wFxqa#6231921 - McGarnagle
如果您指定lambda参数的类型,那么Lambda函数在这些情况下可以很好地工作,但是再次说明通常没有明确指定类型参数更容易。 - Kyle Delaney

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接