c# - 为什么不能从与委托(delegate)方法相同的泛型定义的参数中推断出泛型类型?

标签 c# generics delegates type-inference

我不明白为什么无法推断出对GenericMethod<T>()的调用。我的实际问题包含更多通用参数,从而使调用代码非常冗长且难以阅读。

如果参数本身是'T',则可以在没有显式类型参数的情况下调用GenericMethod<T>(),但是约束不适用于委托,并且一旦您输入了受约束的类型参数,就回到第一个平方。

delegate void GenericDelegate<T>(T t);

static void GenericMethod<T>(GenericDelegate<T> _) { }

static void IntMethod(int _) { }

static void CallingMethod()
{
    // The type arguments for method 'GenericMethod<T>(GenericDelegate<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
    GenericMethod(IntMethod);
}


是否有另一种方法可以使调用代码像此处所述一样简单? GenericMethod<T>()是内部库的一部分。

最佳答案

我不明白为什么无法推断出对GenericMethod<T>()的调用。


这个问题有点含糊。让我尝试整理一下。


  对于参数是方法组的泛型方法类型推断,C#规范怎么说?


推断分两个阶段进行。在第一阶段,从已知参数类型的匿名函数参数和带有类型的参数进行推论。方法组没有类型,因此在第一阶段不对方法组进行推论。

在第二阶段,从lambda和方法组进行推论,其中目标类型是委托类型,该委托类型的输入类型已经被推断。

在您的示例中,输入类型为T,尚未推断出,因此不会进行推断。

这就是类型推断失败的原因。

该答案可能不令人满意,但这是因为您提出了一个模糊的问题。让我们再问一个后续问题,以尝试更清晰地回答您的问题:


  是什么证明了设计决策的正确性,即在类型推断考虑方法组的证据之前必须先推断输入类型?


我们之所以做出此决定,是因为方法组的证据是通过首先从用户要引用的组中确定确切的方法而得出的。我们如何确定该方法?用相同的方法,我们可以确定在任何其他情况下方法组中要使用的方法。我们对该方法组进行重载解析。但是重载解析需要知道参数的类型,这意味着在分析方法组时必须知道输入类型。

例如,如果我们有void M<A, B>(A a, Func<A, B> f){}string N(int y)以及M(123, N),那么我们首先确定在第一阶段Aint,现在Func<A, B>中的输入是已知的,因此重载分辨率可以确定表示string N(int)是预期的,因此Bstring

现在您的后续问题肯定是:


  但是在我的情况下,方法组只有一个成员。编译器不能简单地跳过重载解析并确定我的意思是该组中唯一的方法吗?


您真正要说的是,您需要两种不同的规则,一种针对您的情况而量身定制,而另一种通常有效。

首先,C#设计团队试图避免为狭义的罕见情况创建规则的情况。类型推断的重点不是使用技巧和试探法进行所有可能的推断。目的是要原则上并尽可能使用现有算法。而且,推理算法已经很复杂。我们不要使其变得更复杂。

但是,哦,情况变得更糟。假设C#设计团队决定实施规则“如果方法组包含一个方法,则跳过超载解析”。我想那会让您暂时满意,但是连锁效应是什么?我们刚刚创建了一个炸弹,当您在方法组中添加第二个方法时,该炸弹将在以后爆炸。不幸的是,这使添加第二种方法可能会破坏不相关位置的变化。

而且,您可能不会很满意。 “我添加了第二种方法,但是它具有不同的含义,因此请再次更改重载分辨率,以丢弃错误的含义的方法,直到只剩下一个为止”……依此类推。再次,设计,指定,实现,测试,记录和解释算法变得越来越复杂,越来越难。当然,随着这些算法变得越来越复杂,它们变得更有可能按照用户的意图进行操作,但是当他们不这样做时,用户就会被一大堆规则理解。 (有人可能会说我们已经在那儿了,所以不要让它变得更糟。)

简而言之:原则上,当然可以进行所需的扣除。编译器团队选择不这样做,以使推理算法为人类所理解。


  为什么使用lambda会有什么不同?编译:GenericMethod((int v) => IntMethod(v))


现在,您应该拥有必要的信息来回答该问题,但需要说明:


lambda内IntMethod上的重载解析知道v的类型是int,因此重载解析成功。
现在重载解析成功了,我们知道lambda是int => void,并且可以推断void GenericDelegate<T>(T t)


如果您改为编写GenericMethod(v => IntMethod(v))类型推断,该推断将再次失败。在知道v的类型之前,无法分析lambda的主体,并且v的类型取决于推断出的T的类型,这是我们首先要尝试的方法,因此,推理算法没有任何进展,而且失败了。


  是否有另一种方法可以使调用代码像此处所述一样简单?


不。只需插入<int>并完全跳过类型推断。或将参数强制转换为适当的GenericDelegate类型,这将使​​重载解析在给定泛型委托中的类型的方法组上进行操作。

关于c# - 为什么不能从与委托(delegate)方法相同的泛型定义的参数中推断出泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58592183/

相关文章:

java - 警告 : [unchecked] unchecked cast while using Generic Array

java - 将 Iterable<T> 转换为 T[] 的可重用方法?

ios - 更改委托(delegate)方法的参数类型

c# - C# 中自定义委托(delegate)的示例用法

c# - 如何将发件人地址设置为其他gmail中的任何电子邮件(通过Gmail在.NET中发送电子邮件)?

c# - BackgroundWorker 的底层是如何工作的?

c# - 在 Visual Studio 中制作属性代码片段

c# - 访问 Microsoft.Office.Core.DocumentProperties 的性能问题

java - 将泛型参数传递给类的方法或将方法的结果转换为给定的泛型类型?

ios - 搜索栏委托(delegate)