我们的内部审计建议我们使用显式变量类型声明,而不是使用关键字var
。他们认为使用var
“可能会在某些情况下导致意外结果”。
一旦代码编译成MSIL,我不知道显式类型声明和使用var
之间有什么区别。
审核员是一位受人尊敬的专业人士,所以我不能简单地拒绝这样的建议。
答案 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
循环内。
在下面的示例中,发生从object
到XmlNode
的隐式转换(非通用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