为什么var会变坏?

时间:2009-02-13 11:26:02

标签: c# coding-style implicit-typing

前几天我和同事聊天,听说他们的编码标准明确禁止他们在C#中使用var关键字。他们不知道为什么会如此,我总是发现隐式声明在编码时非常有用。我从来没有遇到任何问题,找出变量的类型(你只是将鼠标悬停在VS中的变量上,然后你就会得到那种类型)。

有谁知道为什么在C#中使用var关键字是个坏主意?

17 个答案:

答案 0 :(得分:63)

2008年11月发布的.Net Framework Design Guidelines(很棒的书)的作者建议在类型明显且明确时使用var

另一方面,如果在阅读代码时使用var会导致歧义,正如Anton Gogolev指出的那样,那么最好不要使用它。

在书(附件A)中,他们实际上给出了这个例子:

var names = new List<string>(); // good usage of var

string source = GetSource();
var tokens = source.Split(' '); // ok; most developers know String.Split

var id = GetId(); // Probably not good; it's not clear what the type of id is

为了确保可读性不受低级开发人员的影响,您的组织可能已经决定您不配var并禁止它。
但这很遗憾,就像拥有一个漂亮的工具,但把它放在一个锁着的玻璃柜里。

在大多数情况下,对简单类型使用var实际上有助于提高可读性,我们不要忘记使用var也不会造成性能损失。

答案 1 :(得分:33)

var q = GetQValue();

确实是一件坏事。然而,

var persistenceManager = ServiceLocator.Resolve<IPersistenceManager>();

对我来说完全没问题。

底线是:使用描述性标识符名称,你会相处得很好。

作为旁注:我不知道如果不允许使用var关键字,他们如何处理匿名类型。或者他们不完全使用它们?

答案 2 :(得分:17)

在大多数情况下,当合理使用时(即类型和值相同的简单类型初始化程序),那就没问题了。

有些时候不清楚你是通过改变它来破坏东西 - 主要是,当初始化类型和(原始)变量类型不相同时,因为:

  • 该变量最初是基类
  • 该变量最初是一个接口
  • 该变量最初是具有隐式转换运算符的另一种类型

在这些情况下,您可能会遇到任何类型分辨率的问题 - 例如:

  • 两种竞争类型具有不同重载的方法
  • 为两种竞争类型定义不同的扩展方法
  • 已在其中一种类型
  • 上重新声明(隐藏)的成员
  • 泛型类型推断将以不同的方式工作
  • 运算符解析将以不同的方式工作

在这种情况下,您可以更改代码的含义,并执行不同的操作。这是一件坏事。

示例:

隐式转换:

static void Main() {
    long x = 17;
    Foo(x);
    var y = 17;
    Foo(y); // boom
}
static void Foo(long value)
{ Console.WriteLine(value); }
static void Foo(int value) {
throw new NotImplementedException(); }

方法隐藏:

static void Main() {
    Foo x = new Bar();
    x.Go();
    var y = new Bar();
    y.Go(); // boom
}
class Foo {
    public void Go() { Console.WriteLine("Hi"); }
}
class Bar : Foo {
    public new void Go() { throw new NotImplementedException(); }
}

答案 3 :(得分:15)

当然这是一个错误。这是因为有些人没有意识到它实际上是强类型的,而且根本不像VB中的var。

并非所有公司编码标准都有意义,我曾经为一家想要在公司名称前加上所有类名称的公司工作过。当公司改名时,进行了大规模的返工。

答案 4 :(得分:8)

几个月前,我写了一篇关于这个主题的博客文章。对我来说,我尽可能地使用它,并专门围绕类型推断设计我的API。我使用类型推断的基本原因是

  1. 不降低类型安全性
  2. 通过提醒您隐式转换,它实际上会增加代码中的类型安全性。 foreach声明中最好的例子
  3. 在C#中维持DRY原则。这是专门针对声明案例,为什么还要两次说这个名字?
  4. 在某些情况下,它需要完全展开。示例匿名类型
  5. 减少打字而不会丢失功能。
  6. http://blogs.msdn.com/jaredpar/archive/2008/09/09/when-to-use-type-inference.aspx

答案 5 :(得分:7)

首先,作为一般规则,编码标准应该由团队讨论并达成一致,并且应该写下他们背后的推理,以便任何人都能知道他们为什么在那里。它们不应该是一位大师的圣洁真理。

其次,这条规则可能是合理的,因为代码的读取次数比写入的次数多var加快了写作速度,但可能会减慢读数速度。它显然不是像“始终初始化变量”这样的代码行为规则,因为两个备选方案(编写var并编写类型)具有完全相同的行为。所以这不是一个关键的规则。我不会禁止var,我只会使用“首选...”

答案 6 :(得分:5)

var是最新的“如何布置你的大括号”/匈牙利符号/骆驼套管辩论。没有正确答案,但也有人坐在极端。

你的朋友很幸运,他们在一个极端分子之下工作。

答案 7 :(得分:5)

禁止它完全意味着禁止使用匿名类型(当你更多地使用LINQ时,它会变得非常有用)。

这是愚蠢的简单和简单,除非有人能够正式确定永远不会使用匿名类型的正当理由。

答案 8 :(得分:4)

如果误用,可能会损害可读性。但是完全禁止它有点奇怪,因为没有它,你的同事将很难使用匿名类型。

答案 9 :(得分:3)

这实际上是您的代码的可读性问题。

我个人的偏好是只对匿名类型使用“var”(实际上,如果你想使用匿名类型,你需要使用var),这些主要来自LINQ查询。在这些情况下,如果您的查询投射到新的(隐式和匿名)类型,则别无选择,只能使用var。

但是,C#3.0很乐意让你在LINQ和匿名类型之外的任何地方使用var,例如:

var myint = 0;
var mystring = "";

完全有效,myint和mystring将通过用于初始化它们的推断值强类型化。 (因此,myint是System.Int32,mystring是System.String)。当然,在查看用于初始化变量的值时,相当明显的是它们将被隐式键入的类型,但是,如果将上述内容编写为:

,我认为它对代码可读性更好。
int myint = 0;
string mystring = "";

因为您可以立即看到这些变量的确切类型。

考虑这个有点令人困惑的情况:

var aaa = 0;
double bbb = 0;

完全有效的代码(如果有点非常规)但在上面,我知道bbb是双精度的,尽管初始化值看起来是一个int,但是aaa肯定不是双精度,而是一个int。 / p>

答案 10 :(得分:3)

您可能认为Microsoft的观点是相关的,因为C#是他们的语言:

  

&#34;但是,使用 var 确实至少有可能使您的代码更难以理解为其他开发人员。因此,C#文档通常仅在需要时使用 var 。&#34;

请参阅MSDN - Implicitly Typed Local Variables (C# Programming Guide),最后一段。


您还应该知道 var 会删除初始分配的编译时数据类型测试。

var x = "mistake";     // error not found by compiler
int x = "mistake";     // error found

由于大多数变量仅分配一次,因此一致使用 var 会删除对变量赋值的几乎所有数据类型测试。

这使您的代码容易受到意外更改,例如那些由合并工具或疲惫的开发人员制作的。

答案 11 :(得分:2)

来自Department of Declaration Redundancy Department(来自杰夫的Coding Horror):

  

“我使用隐式变量输入   随时随地创建我的代码   更简洁。任何删除的东西   我们的代码应该是冗余   积极追求 - 达到和   包括切换语言。“

我自己认为它 值得一试,但是创建一个关于何时使用或不使用的综合指南将矫枉过正

答案 12 :(得分:2)

隐式打字非常棒,而那些违规的人会禁止它会损害生产力并引发脆弱的代码。

这几乎就像是类型安全的,编译器检查的鸭子打字,这在重构时非常有用。例如,如果我有一个返回List的方法,并且我重构它以返回IEnumerable,那么使用var关键字并且只使用IEnumerable方法的那个方法的任何调用者都可以。如果我已明确指定,例如List,那么我必须将其更改为IEnumerable。

显然,如果任何隐式类型调用者需要List方法,那么在构建时我会遇到编译错误,但如果是这种情况,我可能不应该更改返回类型。

答案 13 :(得分:2)

Eric Lippert sums it up well

  • 必要时使用var;当你使用匿名类型时。
  • 当初始值设定项中的声明类型很明显时,使用var,特别是如果它是对象创建。这消除了冗余。
  • 如果代码强调变量的语义“业务目的”并忽略其存储的“机械”细节,请考虑使用var。
  • 如果需要正确理解和维护代码,则使用显式类型。
  • 无论您是否使用“var”,都使用描述性变量名称。变量名称应表示变量的语义,而不是其存储的详细信息; “decimalRate”很糟糕; “interestRate”很好。

我自己的观点:我发现阅读起来比较难,而intstringbool甚至是User等类型都有点无意义。毕竟这是关于可读性的(除了在LINQ中使用它的情况除外),所以当vars被大肆宣传时,它可能更难以阅读并且无法达到语言设计者所希望的关键字的目的。

答案 14 :(得分:1)

我有一些情况(当我通过Table.Rows集合时),当使用var导致类型是某些基类而不是实际的DataRow类型时。这是我唯一一次遇到var。

答案 15 :(得分:0)

<强>&#39;无功&#39;是清楚

关于是否使用var关键字的主要争论是关于代码对您和其他开发人员的可读性。

就像你在写一个故事一样,没有明确的正确答案。但是,让我们用简单的英语来看一些这方面的例子。

  杰克向比尔打招呼。他不喜欢他,所以他转过身去了另一条路。

谁走了另一条路?杰克还是比尔?在这种情况下&#34; Jake&#34;和&#34;比尔&#34;就像类型名称。并且&#34;他&#34;和&#34;他&#34;就像var关键字一样。在这种情况下,它可能有助于更具体。以下例如更清楚。

  杰克向比尔打招呼。杰克并不喜欢比尔,所以他转过身去了另一条路。

在这种情况下更具体地使句子更清楚。但情况并非总是如此。在某些情况下,具体化使得阅读更加困难。

  比尔喜欢看书,所以比尔去了图书馆,比尔拿出了一本比尔一直喜欢的书。

在这种情况下,如果我们使用&#34;他会更容易阅读句子。并且在某些情况下,他们一起遗漏了他的名字,这相当于使用var关键字。

  比尔喜欢书,所以他去了图书馆并拿出了他一直喜欢的书。

这些类比涵盖了要点,但他们并不能讲述整个故事。在这些例子中,只有一种方式可以引用这个人。无论是他们的名字,比如比尔,还是通过更一般的方式,比如&#34;他&#34;和#34;他&#34;。但我们只用一个词来工作。

对于代码,您有两个&#34;单词&#34;,类型和变量名称。

Person p = GetPerson();

现在的问题是,是否有足够的信息可以让您轻松确定p是什么?你还会知道在这种情况下人们是什么:

var p = GetPerson();

这个怎么样:

var p = Get();

这个怎么样:

var person = Get();

或者这个:

var t = GetPerson();

或者这个:

var u = Person.Get();

关键字var在给定方案中是否有效取决于代码的上下文,例如变量,类和方法的名称,以及代码的复杂性。 / p>

就我个人而言,我喜欢使用var关键字,对 me 更全面。但我也倾向于在类型之后命名我的变量,所以我并没有真正丢失任何信息。

那说有时候我会做出例外,这就是任何复杂事物的本质,如果不复杂,软件就没有任何意义。

答案 16 :(得分:-3)

以下是针对var与显式输入的效率进行测试的结果:

  private void btnVar_Click(object sender, EventArgs e)
    {
        Stopwatch obj = new Stopwatch();
        obj.Start();
        var test = "Test";
        test.GetType();
        obj.Stop();
        lblResults.Text = obj.Elapsed.ToString();
    }

    private void btnString_Click(object sender, EventArgs e)
    {
        Stopwatch obj = new Stopwatch();
        obj.Start();
        string test = "Test";
        obj.Stop();
        lblResults.Text = obj.Elapsed.ToString();

    }

First Label结果为:00:00:00 000034

第二个标签结果是:00:00:00 00008