【问题标题】:Type inference doesn't work类型推断不起作用
【发布时间】:2012-01-05 14:36:36
【问题描述】:
class Sample
{
    public static T M<T, TParam1>(TParam1 param1)
    {
        return default(T);
    }
}
class Program
{
    static void Main(string[] args)
    {

        double d = Sample.M((int)121);
    }
}

此代码无法编译并导致以下错误消息:

方法的类型参数 'ThreadPoolTest.Sample.M(TParam1)' 不能从 用法。尝试指定类型参数 明确的

为什么在这个例子中类型推断不起作用?

【问题讨论】:

  • 你是说推理吗?
  • @kbok 我认为这是干扰:D
  • 哦,是的,我的意思是“推理”。感谢修复! :)

标签: c# type-inference


【解决方案1】:

类型推断只能使用参数来进行方法调用。就类型推断而言,您将结果分配给 double 的事实完全无关紧要。换句话说,就编译器而言,它需要弄清楚这意味着什么:

Sample.M((int) 121)

没有更多信息。例如,您可能指的是 Sample.M&lt;int, int&gt;Sample.M&lt;double, int&gt;Sample.M&lt;string, int&gt; - 没有任何信息,所以请说出哪一个更匹配。

你没有在参数列表中提到T,所以类型推断无济于事。

【讨论】:

  • 感谢您的解释!遗憾的是 C# 不明白,我的意思是变量的类型(在这种情况下为双精度)。我认为在大多数情况下,预期的表达类型的确切含义是显而易见的。如果需要特殊类型,则可以像这样显式指定非常简单: double d = (string)Sample.M((int)121);至少部分类型分配是可取的:double d = Sample.M((int)121);
【解决方案2】:

乔恩的回答当然是正确的。考虑一下为什么 C# 在进行推理时不考虑“返回类型”是很有启发性的。这里的基本原则是类型信息从inside流向outside,而不是从outside流向inside。表达。

在您的特定情况下,预期的返回类型是什么是非常明显的,因为您正在分配明确为双精度的东西。但也有很多情况并不明显:

static R M<A, R>(A a) { return default(R); }
static void N(int x, double y) {}
static void N(double x, int y) {}
...
N(M(123), 456);

好的,现在呢?如果有关“分配给什么”的类型信息必须in 流入 M 上的类型推断,那么必须流入的类型信息是“它可以是 int 或 double”。

但是等等,这是正确的吗?我们可以在这里对可能的返回类型进行更多的逻辑推断。

如果它是 int,那么我们有 N(M&lt;int, int&gt;(123), 456) 并且重载决议 on N 将失败,因为该调用不明确;它可能是 N 的任何一个版本!

因此它不能是 int ,对吗?它必须是双倍的,因为这意味着调用是N(M&lt;int, double&gt;(123), 456),这无疑是第二个重载。

现在想象一下,对于 Q(R(S(N(M(... ; 很难正确实现,用户也很难理解他们的程序做了什么,为什么会产生错误,以及如何修复它们。

简单地说表达式的类型分析必须根据其内容而不是其上下文来确定要容易得多。这就是我们所做的。您向编译器抛出的重载解决问题必须是可以解决的,而无需查看问题的上下文;我们只看参数列表的内容来解决问题,而参数列表中没有足够的信息。

现在,lambda 是个例外。 Lambda 参数类型是从它们的上下文中推断出来的,因此,您可以轻松地强制编译器为深度嵌套的 lambda 尝试数万亿种可能的类型分配,以解决重载解决问题。但是我们不想将分析 lambdas 的困难扩展到整个语言;我们希望将这些困难限制在需要它们的特定语言功能上。

【讨论】:

  • 如果您意外陷入需要进行万亿次评估的情况,Visual Studio ide 会做什么?是否有某种超时来阻止机器/核心锁定?我不知道如何构建代码示例来演示它,否则我会自己尝试。
  • @asawyer:试试这个。假设您有类型 X 和 Y,方法 A(Action)、A(Action(Y))、B(Action)、B(Action)、C... 等等,n其中,还有一个带有 n 个参数的方法 M。假设 n 为 3,M 为 M(X, Y, X)。构造一个调用 A(a=>B(b=>C(c=>M(a, b, c)))。重载解析如何解决对 A、B、C 和 M 的调用?它必须测试所有可能的“a 是 X 或 Y,b 是 X 或 Y,c 是 X 或 Y”的组合,以确定 X、Y、X 是 唯一的最佳匹配。这 2 到 3 个可能组合。但是您可以通过更深的嵌套使“3”任意大。
  • @asawyer:当然,如果你有 Action、Action、Action、Action 的重载,那么你会得到 4 到 n 个可能的组合。如果您有 m 个重载和 n 个嵌套,则编译器必须尝试 m 对 n 个 lambda 参数的 n 个可能的估值。如果 m 和 n 都是 10,那就是 100 亿个组合。这让 Visual Studio非常不高兴
  • @asawyer:当然,没有人写这样的代码。但在早期版本的 LINQ 中,“选择多”查询确实会生成与此完全相同的嵌套 lambda,并且超过 8 级深度的多选择查询会彻底弄乱编译器。这就是我们在 LINQ 查询中引入“透明标识符”作为一种机制的原因;它允许我们取消嵌套所有这些 lambda,这样我们就不会在每次有人执行多选查询时解决这个荒谬的重载解决问题。 (嵌套移动到透明标识符的类型中。)
  • 像往常一样,你的帖子读起来很有趣。非常感谢您的详细回复。
【解决方案3】:

因为你没有指定返回类型。

这就是你想要的:

double d = Sample.M<double, int>((int)121);

【讨论】:

  • 实际上显式 (int) 强制转换是多余的。
  • 即使未指定TParam1,文字121 也是int
猜你喜欢
  • 2023-03-05
  • 1970-01-01
  • 2020-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-15
相关资源
最近更新 更多