private void myMethod(var myValue) {
// do something
}
private void myMethod(var myValue) {
// do something
}
var
作为变量。此外,变量必须在声明时被赋值,并且必须能够从右侧表达式中明确地推断出类型。var
的局部变量的类型。如果允许在任何地方使用var
,那么它将需要大幅改变编译器的工作方式。abstract void anotherMethod(double z, double w);
void myMethod<T>(T arg)
{
anotherMethod(arg, 2.0); // Now a compiler could in principle infer that arg must be of type double (but the actual C# compiler can't)
}
在原则上,具有“var”方法参数与通用方法是相同的:
void myMethod<T>(T arg)
{
....
}
很遗憾,你不能仅使用相同的语法,这可能是因为C#的类型推断是后来才添加的。
通常,语言语法和语义上微小的变化会将“确定性”的类型推断算法转变为不可判定的算法。
Operator '*' cannot be applied to operands of type 'T' and 'double'
。 - Anthony Pegram因为编译器通过查看赋值语句右侧的内容确定实际类型。例如,在这里,它被确定为字符串:
var s = "hello";
这里被确定为Foo
:
var foo = new Foo();
ML、Haskell、Scala、F#、SML等语言可以轻松地从其自身语言的等效表达式中推断类型,主要是因为它们从一开始就考虑了类型推断。C#并非如此,它的类型推断是作为解决访问匿名类型问题的事后补救措施而添加的。
我猜想,在C#中没有真正实现Hindley-Milner类型推断,因为在一个如此依赖类和继承的语言中推导类型很复杂。假设我有以下类:
class Base { public void Print() { ... } }
class Derived1 : Base { }
class Derived2 : Base { }
现在我有这个方法:
var create() { return new Derived1(); }
void doStuff(var someBase) { someBase.Print(); }
void Main()
{
doStuff(new Derived1());
doStuff(new Derived2()); // <-- type error or not?
}
doStuff(new Derived1())
,可能会将 doStuff
强制转换为类型 doStuff(Derived1 someBase)
。现在假设我们推断出具体类型而不是泛型类型 T
。doStuff(new Derived1())
怎么办?它是类型错误吗,还是我们通用化为 doStuff<T>(T somebase) where T : Base
?如果我们在一个单独的、未被引用的程序集中进行相同的调用,类型推断算法将不知道是否使用窄类型或更通用的类型。因此,我们将根据方法调用是来自程序集内部还是外部而得到两个不同的类型签名。doStuff
会被限制为接受 Derived1 类型,第二个调用会导致类型错误。class Whatever
{
void Foo() { DoStuff(new Derived1()); }
void Bar() { DoStuff(new Derived2()); }
void DoStuff(var x) { ... }
}
DoStuff
的类型是什么?根据上面的内容,我们知道Foo
或Bar
方法中有一个包含类型错误,但你能从外观上看出哪个有错误吗?Foo
方法)决定了类型,因此Bar
存在错误。// Whatever.cs
class Whatever
{
public void DoStuff(var x);
}
// Foo.cs
class Foo
{
public Foo() { new Whatever().DoStuff(new Derived1()); }
}
// Bar.cs
class Bar
{
public Bar() { new Whatever().DoStuff(new Derived2()); }
}
在C#和VB.NET中,“var”关键字用于类型推断 - 你基本上告诉C#编译器:“你来确定类型吧”。
“var”仍然是强类型的 - 你只是懒得自己写出类型,让编译器根据赋值语句右侧的数据类型来确定。
在这里,在方法参数中,编译器无法确定你真正想表达的意思。怎么办?你真正想表达的是什么类型?编译器无法从方法定义中推断出类型 - 因此它不是一个有效的语句。
因为C#是类型安全和强类型语言。在程序的任何位置,编译器都知道你正在使用的参数类型。var
关键字只是为了引入匿名类型的变量。
检查 C# 4 中的 dynamic
类型推断是指在本地表达式或全局/过程间中的类型推断。因此,它并不是关于“没有正确的右侧”,因为在编译器理论中,过程调用是一种“右侧”形式。
如果编译器进行全局类型推断,C#可以做到这一点,但它没有。
如果您想要接受任何内容的参数,则可以使用“object”,但是然后您需要自己处理运行时转换和潜在异常。
C#中的“var”不是运行时类型绑定,而是编译时特性,最终得到一个非常具体的类型,但C#类型推断的范围有限。