【问题标题】:Is the C# compiler unable to infer method type parameters by expected return type?C# 编译器是否无法通过预期的返回类型推断方法类型参数?
【发布时间】:2011-01-12 23:28:47
【问题描述】:

这对我来说似乎很奇怪,但我记得 Eric Lippert 评论 C# 无法(通过设计,或至少是约定,我认为)基于返回类型重载方法的线程,所以也许它以某种令人费解的方式与此有关。

是否有任何原因这不起作用:

public static T Test<T>() where T : new()
{
    return new T();
}

// Elsewhere
SomeObject myObj = Test();

但这确实:

 var myObj = Test<SomeObject>();

从某种角度来看,它们都很好,因为您没有重复自己(以非常小的方式),但这只是编译器的不同阶段吗?

【问题讨论】:

  • 协方差/逆变?我认为您可能必须说明您所指的 C# 版本。 bit.ly/gG4Iis编辑:我错了; C++ 也不能基于返回类型重载。毕竟,返回类型可以完全省略,所以我认为编译器没有可靠的方法来检测正确的函数。
  • 我假设在提出协变/逆变时,您正在处理返回类型解析;正如我想的那样,这几乎是一个红鲱鱼,但仍然值得一提。

标签: c# type-inference


【解决方案1】:

首先,这是“基于返回类型的重载”:

void M(int x){...}
int M(int x){...}
string M(int x){...}

声明不合法;您不能基于返回类型重载方法,因为返回类型不是签名的一部分,并且签名必须是唯一的。

你说的是基于方法返回类型的方法类型推断。我们也不支持。

原因是因为返回类型可能是你想要弄清楚的

M(Test());

Test 的返回类型是什么?这取决于我们选择的 M 过载。我们选择什么样的 M 过载?这取决于 Test 的返回类型。

一般来说,C# 被设计成每个子表达式都有一个类型,类型是从“内”到“外”计算出来的,而不是从外到内。

值得注意的例外是匿名函数、方法组和 null:

M(x=>x+1)

x=>x+1 的类型是什么?这取决于调用了 M 的哪个重载。

M(N); // N is a method group

N 的类型是什么?同样,这取决于调用了 M 的哪个重载。

等等。在这些情况下,我们会从“外部”到“内部”进行推理。

涉及 lambda 的类型推断极其复杂且难以实现。我们不希望在整个编译器中出现同样的复杂性和困难。

【讨论】:

  • 对不起,我绝对知道这两者的定义和区别,但是重新阅读,简洁使得“在实施动机方面有某种相关性”变成“这些事情接近相同。”不是那个意思。我的问题[应该是]更多关于意图,而不是句法合法性,所以你的最后一段几乎正是我的目标。 :)
【解决方案2】:

除了无类型表达式(null、方法组和 lambda 表达式)外,表达式的类型必须由表达式本身静态确定,而与上下文无关。

换句话说,表达式Test() 的类型不能取决于您将其分配给什么。

【讨论】:

  • 输入条件运算符。我认为您的意思是说“空文字、方法组和匿名函数”。
【解决方案3】:

检查 C# 语言规范 §7.5.2,变量的声明类型不是类型推断的证明,显然它不应该。考虑以下代码:

Base b = Test<Derived>();
Derived d = Test<Derived>();

方法的返回类型可能与变量的声明类型不同,因为我们在 C# 中有隐式转换。

【讨论】:

  • 获取检查,如果只是为了引用相关规范部分:)
  • “显然不应该这样”——为什么不呢? Java 允许在不模棱两可的情况下这样做,我认为这是 Java 正确而 C# 错误的极少数情况之一。
  • @Konrad Rudolph:Java 做对了,而 C# 做错了?证明给我看。在我未发布(并且永远不会发布)的个人编程语言中,1+1=3,所以我可以说它是正确的,而 Java/C# 是错误的,因为它们有 1+1=2?这里我说的是 C# 中的东西。
  • “正确”的选择当然是见仁见智。 C# 中的所有设计选择都是仔细考虑许多竞争替代方案的结果。在我看来,这种选择比其他选择更符合 C# 的底层设计原则。 Java的发明者是否有不同的设计原则,我不知道;你必须问他们为什么做出不同的选择。
  • Smartest:好吧,当我说 Java 做得对时,我显然是指 Java 的设计决策优于 C#。有什么可以证明的?该代码有效,这很有帮助,但并不重要。显然人们不同意(例如 Eric),但为了记录,我对他的解释完全不为所动。这是一个很好的技术解释,但它忽略了用户体验,在我看来,这就是语言设计必须迎合的。更重要的是,有优先权(看看 Eric 的回答),所以即使是他的技术解释也没有真正奏效。
【解决方案4】:

编译器的类型推断不使用赋值的“预期类型”作为逻辑的一部分。

所以,类型推断的“考虑范围”不是这样的:

SomeObject myObj = Test();

但是这个:

Test();

而且,这里没有关于预期类型的​​线索。

【讨论】:

    【解决方案5】:

    如果您想要一个为什么表达式的类型需要能够由表达式本身确定的示例,请考虑以下两种情况:

    1. 我们根本不使用返回值 - 我们只是为了它的副作用而调用该方法。
    2. 我们将返回值直接传递给重载方法

    当涉及到泛型类型解析时,使用返回值的“预期类型”会给编译器带来额外的复杂性,而您所获得的只是有时需要显式指定类型,有时你不需要,你是否需要可以根据代码中其他地方不相关的更改进行更改。

    【讨论】:

      猜你喜欢
      • 2018-08-22
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多