为什么会变成一件坏事?

我有一天和同事聊天,听说他们的编码标准明确地禁止他们在C#中使用var关键字。 他们不知道为什么是这样,我总是发现隐式声明在编码时非常有用。 我从来没有发现什么types的variables的问题(你只是hover在VS的variables,你会得到这种types的方式)。

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

在2008年11月发布的.Net框架devise指南 (真棒书)的作者build议考虑使用var当Type是显而易见和明确的。

另一方面,如果使用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并禁止它。
这是一个耻辱,这就像有一个很好的工具在您的处置,但保持在一个locking的玻璃柜。

在大多数情况下,对简单types使用var实际上有助于可读性,我们不能忘记,使用var也没有性能损失。

 var q = GetQValue(); 

确实是一件坏事。 然而,

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

对我来说完全没问题

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

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

在大多数情况下,当明智地使用(即types和值相同的简单types初始值设定项)时,则没有问题。

有些时候,你不清楚是否已经通过改变它而破坏了东西 – 主要是当初始化types和(原始)variablestypes不一样时,因为:

  • 该variables最初是基类
  • 该variables最初是一个接口
  • 该variables最初是另一种带有隐式转换运算符的types

在这些情况下,您可以通过任何types的分辨率来解决问题 – 例如:

  • 两种竞争types有不同重载的方法
  • 两种竞争types的扩展方法是不同的
  • 已经重新申报(隐藏)其中一种types的成员
  • genericstypes推断的工作方式不同
  • 运营商的决议将工作不同

在这种情况下,你改变代码的含义,并执行一些不同的事情。 这是一件坏事。

例子:

隐式转换:

 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(); } } 

等等

当然这是一个错误。 这是因为有些人没有意识到它实际上是强types的,并不像VB中的变种。

并不是所有的公司编码标准都是有道理的,我曾经为一家公司打算把所有类名称加上公司名称。 公司更名时发生了大规模的返工。

首先,一般来说,编码标准应该由团队讨论和认同,并且把他们背后的理由写下来,这样任何人都可以知道为什么他们在那里。 他们不应该是一个圣主的圣洁真理。

其次,这个规则可能是合理的,因为代码被读取的次数多于写入次数var加速写入,但可能会减慢读取一点。 这显然不是像“总是初始化variables”这样的代码行为规则,因为两种select(写入var和写入types)具有完全相同的行为。 所以这不是一个关键的规则。 我不会禁止var ,我只是使用“首选…”

几个月前我写了一篇关于这个话题的博客文章。 对于我来说,我尽可能地使用它,特别是围绕types推断devise我的API。 我使用types推断的基本原因是

  1. 它不减lesstypes安全
  2. 它会实际上提高你的代码的types安全性,提醒你隐式强制转换。 在foreach语句中的最好的例子
  3. 在C#中维护DRY原则。 这是专门为申报案件,为什么麻烦说两次这个名字?
  4. 在某些情况下,它是必需的。 示例匿名types
  5. 减less打字,不损失function。

http://blogs.msdn.com/jaredpar/archive/2008/09/09/when-to-use-type-in​​ference.aspx

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

你的朋友是不幸的,他们在极端主义者之下工作。

禁止它完全意味着禁止使用匿名types(当你使用LINQ的时候,它变得非常有用)。

除非有人能够正式确定永不使用匿名types的好理由,否则这是愚蠢而简单的。

如果被误用,会损害可读性。 然而,完全禁止它是有点奇怪的,因为你的同事将会在没有它的情况下使用匿名types的困难时间。

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

我个人的偏好是只对匿名types使用“var”(事实上,如果你想使用匿名types,你需要使用var),而这些主要来自LINQ查询。 在这些情况下,如果您的查询投影到新的(隐式&匿名)types中,您别无select,只能使用var。

但是,C#3.0会让您在LINQ和匿名types之外的任何地方使用var,例如:

 var myint = 0; var mystring = ""; 

是完全有效的,myint和mystring将被用来初始化它们的推断值强types化。 (因此,myint是一个System.Int32,mystring是一个System.String)。 当然,在查看用于初始化variables的值时,相当明显的是它们将被隐式键入的types,但是,如果上面的代码可读性更好,我认为它更好:

 int myint = 0; string mystring = ""; 

因为您可以立即看到这些variables的确切types。

考虑一下这个有点混乱的场景:

 var aaa = 0; double bbb = 0; 

完全有效的代码(如果有点不合常规)但是在上面,我知道bbb是一个double,尽pipe初始化值看起来像是一个int,但是aaa肯定不是double,而是int。

你可能会认为微软的意见是相关的,因为C#是他们的语言:

“但是, var的使用至less会让你的代码更难以理解,因此,C#文档通常只在需要的时候使用var

请参阅MSDN – 隐式键入的局部variables(C#编程指南) ,最后一段。


你也应该知道, var在初始分配时删除了编译时数据typestesting。

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

由于大多数variables只被赋值一次,所以var的一致使用会删除几乎所有的variables赋值的数据typestesting。

这会使您的代码容易受到意外更改,例如合并工具或累赘开发人员所做的更改。

隐式input是很好的,平坦的人们禁止它破坏生产力并招致脆弱的代码。

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

显然,如果任何隐式types的调用者需要List方法,那么当我编译时,我会得到编译错误,但是如果是这种情况,我可能不应该改变返回types。

Eric Lippert 总结道 :

  • 使用var时,你必须; 当你使用匿名types。
  • 当初始化器中声明的types明显时,使用var,特别是在创build对象时。 这消除了冗余。
  • 如果代码强调variables的语义“商业目的”,并忽略其存储的“机械”细节,则考虑使用var。
  • 如果这样做是正确理解和维护代码所必需的,则使用显式types。
  • 无论使用“var”,都使用描述性variables名称。 variables名应该表示variables的语义,而不是其存储的细节。 “decimalRate”不好; “interestRate”是好的。

我自己的看法:我觉得很难阅读,有点像intstringbool甚至Usertypes没有意义。 毕竟这是关于可读性的(除了在LINQ中使用它的情况外),所以当vars溅起时,读取和击败语言devise者所期望的关键字的目的将会变得更加困难。

从声明冗余部门 (从杰夫的编码恐怖 ):

“我使用隐式variables打字,无论何时何地使我的代码更加简洁,任何从我们的代码中删除冗余的东西都应该积极追求 – 直至并包括切换语言。

我自己认为这值得考虑的,但是要制定一个关于何时使用或不使用的综合指导将会是过分的

我有案件(当我foreach通过一个Table.Rows集合)时使用var导致types是一些基类而不是实际的DataRowtypes。 那是我唯一遇到var的麻烦了。

“var”即将清晰

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

就像你正在写一个故事一样,没有确切的正确答案。 但是让我们以简单的英文来看看这个例子。

杰克和比尔问好。 他不喜欢他,所以他转过身去。

谁走了另一条路? 杰克还是比尔? 在这种情况下,“Jake”和“Bill”就是types名称。 而“他”和“他”就像var关键字。 在这种情况下,它可能有助于更具体。 下面的例子更清晰。

杰克和比尔问好。 杰克不喜欢比尔,所以他转过头去。

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

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

在这种情况下,如果我们使用“he”,并且在某些情况下将他的名字全部排除在外,那么读取这个句子会更容易,这相当于使用了var关键字。

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

这些类比涵盖了这个要点,但是他们没有讲清楚整个故事。 在这些例子中,只有一种方式可以提到这个人。 无论是用他们的名字,比如比尔,还是用更一般的方式,比如“他”和“他”。 但是我们只用一个词来工作。

在代码的情况下,你有两个“单词”,types和variables名称。

 Person p = GetPerson(); 

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

 var p = GetPerson(); 

这个怎么样:

 var p = Get(); 

这个怎么样:

 var person = Get(); 

或者这个:

 var t = GetPerson(); 

或者这个:

 var u = Person.Get(); 

关键字var是否在给定场景中工作,很大程度上取决于代码的上下文,比如variables的名称,类和方法,以及代码的复杂性。

我个人喜欢使用var关键字对我来说更全面。 但是我也倾向于在这个types之后命名我的variables,所以我并没有真正地丢失任何信息。

这有时候说我有例外,这就是任何复杂的本质,而软件也不是什么复杂的东西。

下面是我对var和explicittypes的效率进行testing的结果:

  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(); } 

第一个标签结果是:00:00:00 000034

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