来自'??' 的 C# 类型推断(“var”)赋值空合并运算符
Posted
技术标签:
【中文标题】来自\'??\' 的 C# 类型推断(“var”)赋值空合并运算符【英文标题】:C# type inference ("var") assignment from '??' null-coalescing operator来自'??' 的 C# 类型推断(“var”)赋值空合并运算符 【发布时间】:2017-12-13 00:32:33 【问题描述】:我已经阅读了许多关于 null 合并 ??
运算符的 SO 问题,但似乎没有一个解决以下具体问题,这既不涉及 nullability (here),运算符优先级(here 和here),尤其是隐式转换(here、here、here 和here)。我还阅读了.NET docs(更多here)并尝试阅读offical spec,但遗憾的是无济于事。
就这样吧。以下两行之间的唯一区别是在第二行中使用var
进行类型推断,而在第一行中使用显式类型Random
,但第二行给出了如图所示的错误,而第一行很好。
Random x = new Random() ?? (x = new Random()); // ok
var y = new Random() ?? (y = new Random()); // CS0841
// ^-------- error here
CS0841:在声明之前不能使用局部变量“y”
第二行究竟是什么让结果变得不确定?
从我上面引用的 hubub 中,我了解到 ??
运算符左侧为 null
的可能性引入了对其右侧实际实例化类型的运行时确定的依赖。嗯,好吧,我猜,……那是什么意思?也许这个网站上关于??
运营商的普遍警告应该是某种可怕的警告......
现在归零,我认为var
关键字(与dynamic
非常相反)的全部意义在于它不受这样的运行时考虑的影响,根据定义。
换句话说,即使我们采用保守但完全可以防御的规则“永远不要在任何赋值=
运算符之外进行窥视”,因此我们无法从??
的右侧获得任何有用的信息,然后仅基于左侧,整体结果必须“兼容”Random
。也就是说,结果必须是Random
或更具体的(派生的)类型;它不能更普遍。因此,根据定义,Random
不应该是推断类型,因为 var
的这种编译时使用?
据我所知,使用运行时考虑破坏var
会破坏其目的。这不正是dynamic
的用途吗?所以我想问题是:
dynamic
就不能不破坏静态类型的纯度吗?
通过向开发人员提供可操作的反馈来实现严格的编译时设计,这难道不是强类型的要点之一吗?为什么var
不能只维持严格保守的政策——总是推断出可以静态推断的最具体类型——同时null-coalescing operator 正在根据来自未来的信息做任何它想做的事情?
【问题讨论】:
"结果必须是 Random 或更具体的(派生的)类型;它不能更一般。" 你反过来用object
替换var
就可以了。跨度>
var
不是运行时,而是编译时。 dynamic
是运行时。 dynamic
主要是为了通过 DLR 与动态语言(例如 Python)进行互操作而创建的。
@KennethK。这正是我的观点。你有什么具体意见吗?
@BenVoigt 非常感谢;我在您提到的地方修复了错误,另外还修复了一个错误。这很尴尬,因为信息偏序应该是我的主要专业领域之一。
@GlennSlayden:嗯?不,第 1 行是不可推断的。它编译是因为根本没有推理。存在的是一个无用的额外分配,它在它分配给的变量的初始化之前发生(如果评估)。初始化将始终覆盖分配的值。
【参考方案1】:
这不是运行时考虑。
使用var
声明的变量的编译时类型是其初始化程序的静态类型。 ??
表达式的静态类型是两个操作数的静态类型的公共类型。但是第二个操作数的静态类型是y
的静态类型,这个是未知的。因此整个初始化器的静态类型未知,推演失败。
确实存在初始化一致的类型,但使用 C# 推理规则无法找到它们。
【讨论】:
事实上在这种情况下object
和Random
将是y
的一致类型,所以它是模棱两可的。实际上,可以通过使用两个类来获得任意数量的一致类型,每个类都实现任意数量的接口。无论如何,要分析一段永远不应该使用的代码,需要付出很多努力。【参考方案2】:
当您使用var
时,类型是在编译时计算出来的。因此,当你写这个时:
var y = new Random() ?? (y = new Random());
编译器在编译时无法确定y
的类型是什么,因此开始大喊大叫——??
的左侧是否为空,将在运行时确定。
一个更好的例子是:
public interface IA void Do();
public class A : IA ...
public class B : IA ...
A a = null;
var something = a ?? new B();
something
的类型应该是什么:IA
、A
或 B
?
【讨论】:
这是在乞求问题,为什么它不能确定它什么时候和第一个一样明显。 @novaterata 阅读我的例子,你就会明白为什么。在发布的问题中,这很明显,但在我提供的示例中,一点也不明显。 @CodingYoshi:对于您的示例,运行时null
是什么并不重要。 var something = new A() ?? new B();
的错误表明您的示例由于其他原因无法编译,即任何可能存储在 a
中的对象现在或在运行时或永远不可能成为 B
。强烈排除它,因为 NET 不允许实例的多重继承(类只能具有一种基类型)。 ??
确实允许多重继承 接口 可能统一的复杂情况,通常需要在 ??
的左侧或右侧进行强制转换
@CodingYoshi 我可能夸大了,您的示例可以按原样工作,只需将??
的任一侧转换为(IA)
,因为它是一个接口。但这不适用于转换为 (A)
或 (B)
,因为这些是“强烈排除”的类
@GlennSlayden 是的,只要我们告诉编译器我们期待IA
,它就会起作用,这就是我的重点。以上是关于来自'??' 的 C# 类型推断(“var”)赋值空合并运算符的主要内容,如果未能解决你的问题,请参考以下文章
在 Java / Eclipse 中推断变量类型,如 C# 的“var”
[原创]Scala学习:关于变量(val,var,类型推断)