在变量声明中使用“var”类型

时间:2010-09-07 11:55:36

标签: c# variable-declaration

我们的内部审计建议我们使用显式变量类型声明,而不是使用关键字var。他们认为使用var“可能会在某些情况下导致意外结果”。

一旦代码编译成MSIL,我不知道显式类型声明和使用var之间有什么区别。

审核员是一位受人尊敬的专业人士,所以我不能简单地拒绝这样的建议。

18 个答案:

答案 0 :(得分:50)

这个怎么样......

double GetTheNumber()
{
    // get the important number from somewhere
}

然后在其他地方......

var theNumber = GetTheNumber();
DoSomethingImportant(theNumber / 5);

然后,在将来的某个时刻,有人注意到GetTheNumber只返回整数,因此重构它以返回int而不是double

砰!没有编译器错误,你开始看到意想不到的结果,因为以前的浮点运算现在已成为整数运算而没有任何人注意到。

话虽如此,应该被你的单元测试等抓住,但它仍然是一个潜在的问题。

答案 1 :(得分:22)

我倾向于遵循这个方案:

var myObject = new MyObject(); // OK as the type is clear

var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear

如果SomeMethod的返回类型发生了变化,那么此代码仍将编译。在最好的情况下,您会进一步得到编译错误,但在最坏的情况下(取决于myObject的使用方式),您可能不会。在这种情况下你可能得到的是运行时错误,这可能很难追查。

答案 2 :(得分:14)

有些情况可能会导致意想不到的结果。我自己是var粉丝,但这可能会出错:

var myDouble = 2;
var myHalf = 1 / myDouble;

显然这是一个错误而不是“意外结果”。但它一个陷阱......

答案 3 :(得分:13)

var不是动态类型,只是syntactic sugar。唯一的例外是Anonymous类型。 From the Microsoft Docs

  
    

在许多情况下,var的使用是可选的,只是语法上的便利。但是,当使用匿名类型初始化变量时,如果需要稍后访问对象的属性,则必须将变量声明为var。

  

编译到IL 后没有区别,除非您已明确地将类型定义为与隐含的类型不同(尽管我无法想到您为什么会这样做)。编译器不允许您在任何时候更改用var声明的变量的类型。

来自Microsoft documentation(再次)

  

隐式类型的局部变量是强类型的,就像你自己声明了类型一样,但编译器确定了类型

在某些情况下,var会妨碍可读性。更多Microsoft docs州:

  

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

答案 4 :(得分:8)

在非通用世界中,每当发生隐式转换时,使用var而不是类型时,您可能会遇到不同的行为,例如:在foreach循环内。

在下面的示例中,发生从objectXmlNode的隐式转换(非通用IEnumerator接口仅返回object)。如果您只是使用var关键字替换循环变量的显式声明,则不再发生此隐式转换:

using System;
using System.Xml;

class Program
{
    static void Foo(object o)
    {
        Console.WriteLine("object overload");
    }

    static void Foo(XmlNode node)
    {
        Console.WriteLine("XmlNode overload");
    }

    static void Main(string[] args)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml("<root><child/></root>");

        foreach (XmlNode node in doc.DocumentElement.ChildNodes)
        {
            Foo(node);
        }

        foreach (var node in doc.DocumentElement.ChildNodes)
        {
            // oops! node is now of type object!
            Foo(node);
        }
    }
}

结果是,此代码实际上会产生不同的输出,具体取决于您使用的是var还是显式类型。使用var时,将执行Foo(object)重载,否则Foo(XmlNode)重载将会执行。因此,上述程序的输出是:

XmlNode overload
object overload

请注意,此行为完全符合C#语言规范。唯一的问题是,var推断的结果与您预期的不同(object,并且通过查看代码,这种推断并不明显。

我没有添加IL以保持简短。但是如果你想要,你可以看看ildasm看看编译器实际上为两个foreach循环生成不同的IL指令。

答案 5 :(得分:7)

一个奇怪的说法是永远不应该使用var因为它“在某些情况下可能导致意外结果”,因为C#语言中的微妙之处远比使用{{1}更复杂。 }。

其中一个是匿名方法的实现细节,这些方法可能导致R#警告“访问修改后的闭包”以及与查看代码时非常不同的行为。与可以用几句话解释的var不同,这种行为需要三篇很长的博客文章,其中包括反汇编程序的输出以完全解释:

这是否意味着您也不应该使用匿名方法(即委托,lambdas)和依赖它们的库(如Linq或ParallelFX),因为在某些奇怪的情况下,行为可能不是您所期望的?

当然不是。

这意味着您需要了解您正在编写的语言,了解其局限性和边缘情况,并测试事情是否符合您的预期。排除语言功能的基础是“在某些情况下可能会导致意外结果”,这意味着您只能使用很少的语言功能。

如果他们真的想争论折腾,请让他们证明你的一些错误可以直接归因于var的使用,并且显式类型声明会阻止它们。我怀疑你很快就会收到他们的回复。

答案 6 :(得分:4)

当提出指导方针时,正如审计员必须做的那样,最好是在傻瓜安全方面犯错,这是白色列出良好做法/黑名单不良做法,而不是告诉人们只是明智的根据对手头情况的评估来做正确的事情

如果你只是说“不要在代码中的任何地方使用var”,那么你就会在编码指南中消除很多含糊之处。这应该使代码看起来和感觉更加标准化,而不必解决何时这样做以及何时这样做的问题。

我个人喜欢var。我将它用于所有局部变量。每时每刻。如果结果类型不清楚,那么这不是var的问题,而是用于初始化变量的(命名)方法的问题...

答案 7 :(得分:4)

  

他们认为使用var“可能会导致   在某些情况下出现意外结果“。在某些情况下会出现意外结果”。

如果意外,“我不知道如何阅读代码并弄清楚它在做什么”,那么是的,它可能会导致意想不到的结果。编译器必须根据围绕变量编写的代码知道变量的类型。

var关键字是编译时功能。编译器将为声明输入适当的类型。这就是为什么你不能做的事情:

var my_variable = null
or
var my_variable;

var关键字很棒,因为您必须在代码本身中定义较少的信息。编译器会找出它应该为你做什么。它几乎就像在使用它时总是编程到接口(接口方法和属性由你在var定义的变量的声明空间中使用的定义)。如果变量的类型需要改变(当然是在理由中),您不必担心更改变量声明,编译器会为您处理。这可能听起来像是一件小事,但如果您必须更改函数中的返回值会发生什么,并且该函数将在整个程序中使用。如果你没有使用var,那么你必须找到并替换调用变量的每个地方。使用var关键字,您无需担心这一点。

答案 8 :(得分:1)

在使用var关键字时,我遵循一个简单的原则。如果您事先知道类型,请不要使用var。 在大多数情况下,我使用var和linq,因为我可能想要返回一个匿名类型。

答案 9 :(得分:1)

当你有明显的声明时

最好使用

ArrayList<Entity> en = new ArrayList<Enity>()

使可读性变得复杂

var en = new ArrayList<Entity>()

懒惰,清晰的代码,我喜欢它

答案 10 :(得分:1)

我只在明确变量的类型,或者根本不需要知道类型的地方使用var(例如GetPerson()应该返回Person,{{1}等等)。

我没有将Person_Class用于原始类型,枚举和字符串。我也不会将它用于值类型,因为值类型将通过赋值复制,因此应该显式声明变量的类型。

关于您的审核员评论,我会说添加更多行代码,因为我们每天都在做“在某些情况下导致意外结果”这个论点我们创建的那些错误已经证明了有效性,因此我建议永远冻结代码库以防止这种情况发生。

答案 11 :(得分:0)

var只是使用显式类型声明的简写表示法。

在某些情况下你只能使用var;使用var时,您必须在声明时初始化变量。 您不能将之后具有其他类型的变量分配给变量。

在我看来,许多人倾向于将'var'关键字与VB6中的'Variant'数据类型混淆。

答案 12 :(得分:0)

我看到使用显式变量声明的“唯一”好处是选择好的类型名称,你可以更清楚地说明你的代码的意图(这比其他任何东西都重要)。 var关键字的好处确实是Pieter所说的。

答案 13 :(得分:0)

使用var和明确指定的变量声明的IL输出绝对没有区别(您可以使用反射器证明这一点)。我通常只使用var用于长嵌套泛型类型,foreach循环和匿名类型,因为我喜欢明确指定所有内容。其他人可能有不同的偏好。

答案 14 :(得分:0)

如果您知道类型将是什么,则使用var是惰性代码。阅读起来更简单,更清洁。在查看大量代码时,更容易和更清洁总是更好

答案 15 :(得分:0)

我还认为如果你在没有D的情况下声明你的双打,你将遇到麻烦。当你编译发布版本时,你的编译器可能会剥离double并使它们成为float以节省空间,因为它不会考虑你的精度。

答案 16 :(得分:0)

var将编译为与可指定的静态类型相同的东西。它只是消除了在代码中使用该Type明确的需要。它不是动态类型,不会/不能在运行时更改。我觉得在foreach循环中使用非常有用。

foreach(var item in items)
{
item.name = ______;
}

使用Enumerations时,某些特定类型的查找时间不明。使用var而不是Static Type会得到相同的结果。 我还发现var的使用使其更容易重构。当使用不同类型的枚举时,不需要更新foreach。

答案 17 :(得分:0)

使用var可能会隐藏逻辑编程错误,否则您将收到编译器或IDE的警告。见这个例子:

float distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);

此处,计算中的所有类型均为int,您会收到有关可能的分数损失的警告,因为您在float变量中选取了结果。

使用var:

var distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);

这里没有任何警告,因为distX的类型编译为int。如果您打算使用浮点值,这是一个隐藏给您的逻辑错误,并且在执行时很难发现,除非它在稍后的计算中触发divide by zero异常,如果此初始计算的结果为&lt; 1