我们的内部审计建议我们使用显式变量类型声明,而不是使用关键字var
。他们认为使用var
“在某些情况下可能导致意外的结果”。
一旦代码编译为MSIL,我不知道显式类型声明和使用var
之间有任何区别。
审核员是一位备受尊敬的专业人士,因此我不能简单地拒绝这样的建议。
我们的内部审计建议我们使用显式变量类型声明,而不是使用关键字var
。他们认为使用var
“在某些情况下可能导致意外的结果”。
一旦代码编译为MSIL,我不知道显式类型声明和使用var
之间有任何区别。
审核员是一位备受尊敬的专业人士,因此我不能简单地拒绝这样的建议。
这个怎么样...
double GetTheNumber()
{
// get the important number from somewhere
}
然后在其他地方...
var theNumber = GetTheNumber();
DoSomethingImportant(theNumber / 5);
在将来的某个时候,有人注意到GetTheNumber
只返回整数,因此对其进行了重构,并改为返回int
而不是double
。
糟糕!没有编译器错误,但你开始看到意外的结果,因为之前的浮点运算现在成为了整数运算,却没有被任何人注意到。
话虽如此,这种情况应该被你的单元测试等发现,但它仍然是一个潜在的陷阱。
Console.WriteLine("97 divided by 5 is " + (97 / 5));
和Console.WriteLine("97 divided by 5 is " + ((double)97 / 5));
。 - LukeHvar
关键字的错。而是 curvedHands.dll
的问题。 - Arnis Lapsa我倾向于遵循以下方案:
var myObject = new MyObject(); // OK as the type is clear
var myObject = otherObject.SomeMethod(); // Bad as the return type is not clear
如果SomeMethod
的返回类型发生变化,这段代码仍将保持编译状态。在最好的情况下,您会得到更深入的编译错误,但在最坏的情况下(取决于如何使用myObject
),您可能不会收到任何错误信息。在这种情况下,您可能会遇到运行时错误,这些错误可能非常难以追踪。var不是动态类型,它只是一种语法糖。唯一的例外是匿名类型。来自Microsoft文档
在许多情况下,使用var是可选的,只是一种语法上的便利。但是,当一个变量被初始化为匿名类型时,如果您需要在以后访问对象的属性,则必须将变量声明为var。
编译成IL之后没有区别,除非您明确将类型定义为与暗示的类型不同(尽管我想不出为什么要这样做)。编译器不会允许您在任何时候更改使用var声明的变量的类型。
来自Microsoft文档(再次引用)
隐式类型的本地变量与您自己声明类型一样强类型化,但编译器确定类型
在某些情况下,var可能会影响可读性。更多Microsoft文档指出:
使用var至少有可能使其他开发人员更难理解您的代码。因此,C#文档通常仅在需要时使用var。
有些情况确实会导致意料之外的结果。我自己是一个var
粉丝,但这样可能会出错:
var myDouble = 2;
var myHalf = 1 / myDouble;
显然这是一个错误而不是"意外的结果"。但这确实是一个坑点...
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指令。
System.Xml.Linq
。 - ChaosPandionXmlDocument
,那么使用 Linq 可能并不总是你的选择。此外,XmlDocument
只是一个例子,仍然有很多非泛型代码存在(例如几乎所有 Windows Forms 代码),在这些代码中你会遇到我回答中描述的问题。 - Dirk VollmarIEnumerator
,那么明确的类型是object
。我也遇到过这个问题。另一个解决方案是添加.Cast<YourType>()
,但直接指定类型似乎更简单明了。 - recursiveConvert.ToString()
重载。 - Dirk Vollmarvar
声明实现了你所期望的功能,而显式类型声明则没有。使用var
时需要手动转换类型,而使用显式类型声明时编译器会自动进行类型转换。如果代码出现错误并出现InvalidCastException
异常,使用var
的代码更容易调试以找出其原因,因为你自己写了转换类型的代码;而使用显式类型声明时,你必须知道编译器自动插入了类型转换代码。 - Greg Beech这是一个奇怪的说法,认为使用var
永远不应该被使用,因为它“可能会在某些情况下导致意外结果”,因为C#语言中还有比使用var
更复杂的微妙之处。
其中之一是匿名方法的实现细节,这可能会导致R#警告“访问修改的闭包”和行为与您从代码中看到的完全不同。与可以用几句话解释的var
不同,这种行为需要三篇长博客文章来完全解释,其中包括反汇编器的输出:
这是否意味着您也不应使用匿名方法(即委托、Lambda)以及依赖它们的库,如Linq或ParallelFX,只是因为在某些奇怪的情况下行为可能不符合您的预期?
当然不是。
这意味着您需要了解您正在编写的语言,知道其限制和边缘情况,并测试事物是否按您所期望的方式工作。基于“可能导致某些情况下出现意外结果”的基础上排除语言特性将意味着您只剩下很少的语言特性可用。
如果他们真的想争辩,那就让他们证明你的一些错误可以直接归因于使用var
,而显式类型声明可以防止这些错误。我怀疑你不会很快收到他们的回复。var my_variable = null
or
var my_variable;
var
”,那么就可以消除许多编码准则中的歧义。这应该使代码看起来更加标准化,而无需解决何时应该这样做和何时应该那样做的问题。我个人非常喜欢var
。我将其用于所有本地变量。一直如此。如果产生的类型不清楚,则这不是var
的问题,而是初始化变量的方法(命名)的问题...var
:IDictionary<string,MyVeryLongGenericClassName<int,string,DateTime>>
。 - ChaosPandion当你有明显的声明时,最好使用var。
ArrayList<Entity> en = new ArrayList<Enity>()
复杂化了可读性
var en = new ArrayList<Entity>()
懒惰、简洁的代码,我喜欢它
var
用于 所有 变量可能会使得在没有智能感知的情况下很难弄清楚正在发生什么。 - thecoop
IDictionary<ISomeHideouslyLongKey, IList<ISomeRidiculouslyComplicatedValue>> boogidy = new Dictionary<ISomeHideouslyLongKey, IList<ISomeRidiculouslyComplicatedValue>>();
这样的声明。 - Jesse C. Slicer