我们的内部审计建议我们使用显式变量类型声明而不是使用关键字var
。他们争辩说,使用var
“在某些情况下可能会导致意想不到的结果”。
var
一旦代码编译为 MSIL,我不知道显式类型声明和使用 of 之间有什么区别。
审计师是一位受人尊敬的专业人士,所以我不能简单地拒绝这样的建议。
我们的内部审计建议我们使用显式变量类型声明而不是使用关键字var
。他们争辩说,使用var
“在某些情况下可能会导致意想不到的结果”。
var
一旦代码编译为 MSIL,我不知道显式类型声明和使用 of 之间有什么区别。
审计师是一位受人尊敬的专业人士,所以我不能简单地拒绝这样的建议。
这个怎么样...
double GetTheNumber()
{
// get the important number from somewhere
}
然后在别处...
var theNumber = GetTheNumber();
DoSomethingImportant(theNumber / 5);
然后,在未来的某个时候,有人注意到GetTheNumber
只返回整数,因此将其重构为 returnint
而不是double
.
砰! 没有编译器错误,您开始看到意想不到的结果,因为以前的浮点运算现在已经变成了整数运算,而没有人注意到。
话虽如此,这种事情应该被您的单元测试等捕获,但它仍然是一个潜在的陷阱。
我倾向于遵循这个方案:
var myObject = new MyObject(); // OK as the type is clear
var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear
如果 ever 的返回类型SomeMethod
发生变化,那么这段代码仍然可以编译。在最好的情况下,您会进一步得到编译错误,但在最坏的情况下(取决于myObject
使用方式),您可能不会。在这种情况下,您可能会得到运行时错误,这些错误可能很难追踪。
var 不是动态类型,它只是语法糖。唯一的例外是匿名类型。 来自微软文档
在许多情况下,使用 var 是可选的,只是为了语法方便。但是,当使用匿名类型初始化变量时,如果以后需要访问对象的属性,则必须将该变量声明为 var。
除非您已将类型明确定义为与隐含的类型不同(尽管我想不出您为什么会这样),否则一旦编译为 IL 就没有区别。编译器不会让您在任何时候更改用 var 声明的变量的类型。
来自Microsoft 文档(再次)
隐式类型的局部变量是强类型的,就像您自己声明了类型一样,但编译器确定类型
在某些情况下, var 会影响可读性。更多Microsoft 文档状态:
var 的使用至少有可能使其他开发人员更难以理解您的代码。因此,C# 文档通常仅在需要时才使用 var。
有些情况真的会导致意想不到的结果。我var
自己是粉丝,但这可能会出错:
var myDouble = 2;
var myHalf = 1 / myDouble;
显然这是一个错误,而不是“意外结果”。但这是一个陷阱...
在非泛型世界var
中,每当发生隐式转换时,例如在foreach
循环中,使用而不是类型时,您可能会得到不同的行为。
在下面的示例中,发生了从object
to的隐式转换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 重载 对象重载
请注意,此行为完全符合 C# 语言规范。唯一的问题是它var
推断出的类型 ( object
)与您预期的不同,并且从查看代码来看,这种推断并不明显。
我没有添加 IL 来保持简短。但是,如果您愿意,您可以使用 ildasm 查看编译器实际上为两个 foreach 循环生成了不同的 IL 指令。
一个奇怪的说法是var
永远不应该使用 using,因为它“在某些情况下可能会导致意想不到的结果”,因为 C# 语言中的微妙之处远比使用var
.
其中之一是匿名方法的实现细节,它可能导致 R# 警告“访问修改后的闭包”和行为,这与您在查看代码时所期望的非常不同。与var
可以用几句话解释的不同,这种行为需要三篇很长的博客文章,其中包括反汇编程序的输出才能完全解释:
这是否意味着您也不应该使用匿名方法(即委托、lambda)和依赖它们的库,例如 Linq 或 ParallelFX,因为在某些奇怪的情况下,行为可能不是您所期望的?
当然不是。
这意味着您需要了解您正在编写的语言,了解它的局限性和极端情况,并测试事情是否按您期望的那样工作。以“在某些情况下可能导致意外结果”为由排除语言特性意味着您只剩下很少的语言特性可供使用。
如果他们真的想争论折腾,请他们证明您的一些错误可以直接归因于使用 ofvar
并且显式类型声明会阻止它们。我怀疑你很快就会收到他们的回复。
他们争辩说,使用 var “在某些情况下可能会导致意想不到的结果”。在某些情况下会导致意想不到的结果”。
如果出乎意料的是,“我不知道如何阅读代码并弄清楚它在做什么”,那么是的,它可能会导致意想不到的结果。编译器必须根据围绕变量编写的代码知道要生成什么类型的变量。
var 关键字是一个编译时特性。编译器将为声明放入适当的类型。这就是为什么你不能做这样的事情:
var my_variable = null
or
var my_variable;
var 关键字很棒,因为您必须在代码本身中定义更少的信息。编译器会弄清楚它应该为你做什么。几乎就像总是在使用接口时对接口进行编程(接口方法和属性由您在 var 定义的变量的声明空间中使用的内容定义)。如果变量的类型需要更改(当然是在合理范围内),您无需担心更改变量声明,编译器会为您处理。这听起来可能是一件微不足道的事情,但是如果您必须更改函数中的返回值,并且该函数在整个程序中都被使用,会发生什么情况。如果您没有使用 var,那么您必须找到并替换每个调用该变量的位置。使用 var 关键字,您无需担心这一点。
在制定指导方针时,正如审计员必须做的那样,最好是在傻瓜安全方面犯错,即白名单良好做法/黑名单不良做法,而不是告诉人们简单地明智并做正确的事事情基于对手头情况的评估。
如果您只是说“不要var
在代码中的任何地方使用”,那么您就可以摆脱编码指南中的很多歧义。这应该使代码看起来和感觉更加标准化,而不必解决什么时候做这个和什么时候做那个的问题。
我个人很喜欢var
。我将它用于所有局部变量。每时每刻。如果结果类型不清楚,那么这不是问题var
,而是用于初始化变量的(命名)方法的问题......
我var
只在清楚变量是什么类型或者根本不需要知道类型的地方使用(例如 GetPerson() 应该返回Person
,Person_Class
等)。
我不var
用于原始类型、枚举和字符串。我也不将它用于值类型,因为值类型将通过赋值复制,因此应显式声明变量的类型。
关于您的审核员评论,我想说像我们每天所做的那样添加更多代码行也会“在某些情况下导致意想不到的结果”。我们创建的那些错误已经证明了这个论点的有效性,因此我建议永远冻结代码库以防止这种情况发生。
当你有明显的声明时最好使用 var
ArrayList<Entity> en = new ArrayList<Enity>()
使可读性复杂化
var en = new ArrayList<Entity>()
懒惰,清晰的代码,我喜欢它
在使用 var 关键字时,我遵循一个简单的原则。如果您事先知道类型,请不要使用 var。在大多数情况下,我将 var 与 linq 一起使用,因为我可能想要返回一个匿名类型。
var 将编译为与可以指定的静态类型相同的内容。它只是消除了在代码中明确使用该类型的需要。它不是动态类型,在运行时不会/不能更改。我发现在 foreach 循环中使用它非常有用。
foreach(var item in items)
{
item.name = ______;
}
有时使用枚举时,不知道特定类型的查找耗时。使用 var 而不是静态类型将产生相同的结果。我还发现使用 var 使其更易于重构。当使用不同类型的枚举时,不需要更新 foreach。
我还认为,如果你在最后没有 D 的情况下宣布你的双打,你会遇到麻烦。当您编译发布版本时,您的编译器可能会剥离双精度并使它们成为浮点数以节省空间,因为它不会考虑您的精度。
如果您知道类型将是什么,则使用 var 是惰性代码。它只是更容易和更清洁的阅读。在查看大量代码时,更简单、更简洁总是更好
var
对于使用和明确指定的变量声明,IL 输出绝对没有区别(您可以使用反射器证明这一点)。我通常只var
用于长嵌套的泛型类型、foreach
循环和匿名类型,因为我喜欢明确指定所有内容。其他人可能有不同的偏好。
var 只是使用显式类型声明的简写符号。
您只能在某些情况下使用 var;使用 var 时,您必须在声明时初始化变量。之后您不能将另一个类型的变量分配给该变量。
在我看来,很多人倾向于将 'var' 关键字与 VB6 中的 'Variant' 数据类型混淆。
我看到的使用显式变量声明的“唯一”好处是使用精心选择的类型名,您可以更清楚地说明代码的意图(这比 imo 中的任何其他内容都更重要)。var 关键字的好处确实是 Pieter 所说的。
使用 var 可能会隐藏逻辑编程错误,否则您会收到来自编译器或 IDE 的警告。看这个例子:
float distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);
在这里,计算中的所有类型都是int
,并且您会收到有关可能丢失分数的警告,因为您在float
变量中获取了结果。
使用变量:
var distX = innerDiagramRect.Size.Width / (numObjInWidth + 1);
在这里您不会收到任何警告,因为 的类型distX
被编译为int
. 如果您打算使用浮点值,这是一个对您隐藏的逻辑错误,并且在执行时很难发现,除非它divide by zero
在此初始计算的结果小于 1 时在以后的计算中触发异常。