关于类型推断:在C#中使用var关键字

Use of var keyword in C#

在与同事讨论了C 3中"var"关键字的使用后,我想知道人们对通过var进行类型推断的适当使用有什么看法?

例如,在有问题的情况下,我相当懒惰地使用var,例如:-

1
2
3
foreach(var item in someList) { // ... } // Type of 'item' not clear.
var something = someObject.SomeProperty; // Type of 'something' not clear.
var something = someMethod(); // Type of 'something' not clear.

VaR的更多合法用途如下:

1
2
var l = new List<string>(); // Obvious what l will be.
var s = new SomeClass(); // Obvious what s will be.

有趣的是,Linq似乎有点灰暗,例如:-

1
2
var results = from r in dataContext.SomeTable
              select r; // Not *entirely clear* what results will be here.

很明显,结果会是这样的:它将是一个实现IEnumerable的类型,但是它并不像声明新对象的var那样完全明显。

当涉及到linq to对象时,情况更糟,例如:-

1
2
3
var results = from item in someList
              where item != 3
              select item;

这并不比同等的foreach(somelist中的var项)/…等值。

这里真正关心的是类型安全性——例如,如果我们将查询结果放入一个接受IEnumerable和IEnumerable的重载方法中,调用方可能会无意中传入错误的类型。

var确实保持了强类型,但问题是类型在定义上是否不立即明显是危险的,当重载意味着在无意中将错误类型传递给方法时,编译器错误可能不会被放大。


我仍然认为var在某些情况下可以使代码更可读。如果我有一个带有orders属性的customer类,并且我想将它赋给一个变量,那么我只需要这样做:

1
var orders = cust.Orders;

我不在乎客户。订单是IEnumerableObservableCollectionBindingList——我只想把这个列表保存在内存中,以便对它进行迭代,或者稍后获取它的计数或其他内容。

将上述声明与以下内容进行对比:

1
ObservableCollection<Order> orders = cust.Orders;

对我来说,类型名只是噪音。如果我回去决定改变客户的类型,那么我也需要改变这个声明——如果我一开始使用var的话,我不需要这样做。


我广泛使用var。有人批评这会降低代码的可读性,但没有理由支持这种说法。

诚然,这可能意味着还不清楚我们在处理什么类型的问题。那又怎么样?这实际上是一个分离设计的要点。在处理接口时,您显然对变量的类型不感兴趣。var对此作了更进一步的解释,但我认为从可读性的角度来看,这个论点是相同的:程序员实际上不应该对变量的类型感兴趣,而应该对变量的作用感兴趣。这就是为什么微软还称类型推断为"duck-typing"。

那么,当我使用var声明变量时,它会做什么?很简单,它做任何智能感知告诉我它做的事情。任何对C的推理,如果忽略了IDE,都是不现实的。实际上,每个C代码都是在支持IntelliSense的IDE中编程的。

如果我使用的是一个var声明的变量,并且混淆了变量的用途,那么我的代码就有一些根本性的错误。var不是原因,它只是使症状明显。别怪送信人。

现在,C团队发布了一个编码指南,声明var只应用于捕获创建匿名类型的linq语句的结果(因为这里,我们没有真正的替代var的方法)。好吧,去死吧。只要C小组没有给我一个合理的理由来支持这个指导方针,我会忽略它,因为在我的专业和个人观点中,它是纯粹的胡扯。(抱歉,我没有链接到有问题的准则。)

事实上,有一些(表面上)很好的解释解释为什么你不应该使用var,但我仍然认为它们在很大程度上是错误的。以"可搜索性"为例:作者声称,var使得搜索使用MyType的地方变得困难。正确的。接口也是如此。实际上,我为什么想知道在哪里使用这个类?我可能更感兴趣的是它在哪里被实例化,而这仍然是可以搜索的,因为必须在某个地方调用它的构造函数(即使这是间接完成的,也必须在某个地方提到类型名)。


在我看来,C中的var是一个很好的东西。任何这样类型的变量仍然是强类型的,但它的类型是从定义它的赋值的右侧获取的。因为类型信息在右侧是可用的,在大多数情况下,在左侧也必须输入类型信息是不必要和过于冗长的。我认为这会显著提高可读性,而不会降低类型安全性。

在我看来,从可读性的角度来看,对变量和方法使用良好的命名约定比显式类型信息更重要。如果我需要类型信息,我可以将鼠标悬停在变量(在vs中)上并获取它。不过,一般来说,对于读者来说,显式类型信息不应该是必需的。对于开发人员来说,在vs中,无论变量是如何声明的,您仍然可以获得intellisense。尽管如此,仍然有一些情况下显式声明类型是有意义的——也许您有一个返回List的方法,但您希望在您的方法中将其视为IEnumerable。为了确保您使用的是接口,声明接口类型的变量可以使其显式化。或者,您可能希望声明一个没有初始值的变量——因为它会根据某个条件立即得到一个值。在这种情况下,您需要类型。如果类型信息有用或必要,请继续使用。不过,我觉得通常不需要,而且在大多数情况下,没有它代码更容易阅读。


两者都不是绝对正确的;var对可读性既有正面影响,也有负面影响。在我看来,当下列任一情况属实时,应使用var

  • 类型是匿名的(好吧,这里没有任何选择,因为在本例中它必须是var)
  • 根据指定的表达式(即var foo = new TypeWithAReallyLongNameTheresNoSenseRepeating()),类型是明显的。
  • var没有性能影响,因为它是语法上的制糖;编译器在编译成IL后推断类型并定义它;它实际上没有任何动态性。


    来自C团队高级软件设计工程师Eric Lippert:

    为什么引入var关键字?

    There are two reasons, one which
    exists today, one which will crop up
    in 3.0.

    The first reason is that this code is
    incredibly ugly because of all the
    redundancy:

    Dictionary> mylists = new Dictionary>();

    And that's a simple example – I've
    written worse. Any time you're forced
    to type exactly the same thing twice,
    that's a redundancy that we can
    remove. Much nicer to write

    var mylists = new Dictionary>();

    and let the compiler figure out what
    the type is based on the assignment.

    Second, C# 3.0 introduces anonymous
    types. Since anonymous types by
    definition have no names, you need to
    be able to infer the type of the
    variable from the initializing
    expression if its type is anonymous.

    强调我的。整篇文章,c 3.0仍然是静态类型,诚实!接下来的系列也不错。

    这就是var的目的。其他用途可能不会那么好用。任何与JScript、VBScript或动态类型的比较都是完全错误的。再次注意,为了在.NET中使用某些其他功能,需要使用var


    我认为var的使用应该与明智地选择的变量名相结合。

    在foreach语句中使用var没有问题,前提是它不是这样的:

    1
    foreach (var c in list) { ... }

    如果更像这样:

    1
    foreach (var customer in list) { ... }

    …那么,阅读代码的人更有可能理解"列表"是什么。如果您可以控制列表变量本身的名称,那就更好了。

    这同样适用于其他情况。这很没用:

    1
    var x = SaveFoo(foo);

    …但这是有道理的:

    1
    var saveSucceeded = SaveFoo(foo);

    我想每个人都是自己的。我发现自己在做这件事,简直是疯了:

    1
    var f = (float)3;

    我需要某种12步无功程序。我叫马特,我(ab)用var。


    我们采用了"人的代码,而不是机器的代码"的理念,基于这样一个假设,即您在维护模式上花费的时间比在新开发上花费的时间要长很多倍。

    对我来说,这排除了编译器"知道"变量是什么类型的论点——当然,你不能第一次写无效的代码,因为编译器会阻止你的代码编译,但是当下一个开发人员在6个月的时间内读取代码时,他们需要能够推断出变量做的是正确的还是错误的,并且是正确的。确定问题的原因。

    因此,

    1
    var something = SomeMethod();

    我们的编码标准禁止使用,但我们的团队鼓励使用以下内容,因为它提高了可读性:

    1
    2
    3
    4
    5
    var list = new List<KeyValuePair<string, double>>();
    FillList( list );
    foreach( var item in list ) {
       DoWork( item );
    }


    这还不错,它更像是一种风格化的东西,往往是主观的。它可以增加不一致性,在使用var和不使用var时。

    另一个值得关注的情况是,在下面的调用中,您不能只通过查看代码就知道CallMe返回的类型:

    1
    var variable = CallMe();

    这是我对VaR的主要抱怨。

    当我在方法中声明匿名委托时,我使用var,与使用Func相比,var看起来更干净。考虑此代码:

    1
    2
    3
    var callback = new Func<IntPtr, bool>(delegate(IntPtr hWnd) {
       ...
    });

    编辑:根据Julian的输入更新最后一个代码示例