题
在与同事讨论 C# 3 中“var”关键字的使用后,我想知道人们对通过 var 进行类型推断的适当使用有何看法?
例如,我在有问题的情况下宁愿懒惰地使用 var,例如:-
foreach(var item in someList) { // ... } // Type of 'item' not clear.
var something = someObject.SomeProperty; // Type of 'something' not clear.
var something = someMethod(); // Type of 'something' not clear.
var 更合法的用途如下:-
var l = new List<string>(); // Obvious what l will be.
var s = new SomeClass(); // Obvious what s will be.
有趣的是,LINQ 似乎有点灰色地带,例如:-
var results = from r in dataContext.SomeTable
select r; // Not *entirely clear* what results will be here.
很明显,它将是一个实现 IEnumerable 的类型,但它并不像 var 声明新对象那样完全明显。
当涉及 LINQ to 对象时,情况更糟,例如:-
var results = from item in someList
where item != 3
select item;
这并不比等效的 foreach(var item in someList) { // ...} 等价。
这里确实存在类型安全问题 - 例如,如果我们将该查询的结果放入接受 IEnumerable<int> 和 IEnumerable<double> 的重载方法中,调用者可能会无意中传递错误的类型。
var
做 保持强类型,但问题实际上是类型在定义上不立即明显是否危险,当重载时,这种情况会被放大,这意味着当您无意中将错误的类型传递给方法时,可能不会发出编译器错误。
解决方案
我依然觉得 var
在某些情况下可以使代码更具可读性。如果我有一个带有 Orders 属性的 Customer 类,并且我想将其分配给一个变量,我将这样做:
var orders = cust.Orders;
我不在乎 Customer.Orders 是否是 IEnumerable<Order>
, ObservableCollection<Order>
或者 BindingList<Order>
- 我想要的只是将该列表保留在内存中以对其进行迭代或稍后获取其计数或其他内容。
将上述声明与以下内容进行对比:
ObservableCollection<Order> orders = cust.Orders;
对我来说,类型名称只是噪音。如果我返回并决定更改 Customer.Orders 的类型(比如从 ObservableCollection<Order>
到 IList<Order>
)然后我也需要更改该声明 - 如果我首先使用 var ,我就不必这样做。
其他提示
我用 var
广泛地。有人批评这会降低代码的可读性,但没有证据支持这一说法。
诚然,这可能意味着我们不清楚我们正在处理什么类型。所以呢?这其实就是解耦设计的要点。当处理接口时,你强调 不是 对变量的类型感兴趣。 var
确实如此,但我认为从可读性的角度来看,这个论点仍然是一样的:程序员实际上不应该对变量的类型感兴趣,而应该对变量的用途感兴趣 做. 。这就是为什么微软也将类型推断称为“鸭子类型”。
那么,当我使用声明变量时它会做什么 var
?很简单,它会执行 IntelliSense 告诉我的任何操作。任何忽略 IDE 的关于 C# 的推理都是不现实的。实际上,每个 C# 代码都是在支持 IntelliSense 的 IDE 中编程的。
如果我使用的是 var
声明的变量并且对变量的用途感到困惑,我的代码有根本性的错误。 var
不是原因,它只会使症状变得可见。不要责怪使者。
现在,C# 团队发布了一份编码指南,指出 var
应该 仅有的 用于捕获创建匿名类型的 LINQ 语句的结果(因为在这里,我们没有真正的替代方案 var
)。好吧,管他呢。只要 C# 团队没有给我提供关于该指南的合理论据,我就会忽略它,因为在我的专业和个人看来,这纯粹是胡说八道。(对不起;我没有相关指南的链接。)
事实上,有一些(表面上) 很好的解释 为什么你不应该使用 var
但我仍然相信他们在很大程度上是错误的。以“可搜索性”为例:作者声称 var
使得寻找地方变得困难 MyType
用来。正确的。接口也是如此。实际上,我为什么想知道该类在哪里使用?我可能对它的实例化位置更感兴趣,并且它仍然是可搜索的,因为必须在某个地方调用它的构造函数(即使这是间接完成的,也必须在某个地方提到类型名称)。
在我看来,Var 在 C# 中是 好东西Tm值. 。任何这样类型化的变量仍然是强类型化的,但它从定义它的赋值的右侧获取其类型。由于类型信息位于右侧,因此在大多数情况下,在左侧输入类型信息是不必要的,而且过于冗长。我认为这在不降低类型安全性的情况下显着提高了可读性。
从我的角度来看,从可读性的角度来看,对变量和方法使用良好的命名约定比显式类型信息更重要。如果我需要类型信息,我可以随时将鼠标悬停在变量上(在 VS 中)并获取它。不过,一般来说,读者不需要显式的类型信息。对于开发人员来说,在 VS 中您仍然可以获得 Intellisense,无论变量是如何声明的。话虽如此,在某些情况下,显式声明类型可能仍然有意义——也许您有一个返回 List<T>
, ,但您想将其视为 IEnumerable<T>
在你的方法中。为了确保您正在使用该接口,声明接口类型的变量可以使其显式化。或者,也许您想声明一个没有初始值的变量——因为它会根据某些条件立即获取一个值。在这种情况下,您需要该类型。如果类型信息有用或必要,请继续使用它。不过,我觉得通常没有必要,并且在大多数情况下,没有它代码更容易阅读。
这些都不是绝对正确的。 var
对可读性既有积极的影响,也有消极的影响。在我看来, var
当满足以下任一条件时应使用:
- 该类型是匿名的(好吧,你在这里没有任何选择,因为它 必须 在本例中为 var)
- 根据指定的表达式,类型是显而易见的(即
var foo = new TypeWithAReallyLongNameTheresNoSenseRepeating()
)
var
对性能没有影响,因为它是语法糖;一旦编译成 IL,编译器就会推断类型并定义它;什么也没有 实际上 关于它的动态。
来自 C# 团队的高级软件设计工程师 Eric Lippert:
为什么是 var
引入关键字?
有两个原因,一个今天存在,一种将以3.0的形式出现。
第一个原因是该代码非常丑陋,因为所有冗余:
Dictionary<string, List<int>> mylists = new Dictionary<string, List<int>>();
这是一个简单的例子 - 我写的更糟。每当您被迫两次键入完全相同的东西时,这都是我们可以删除的冗余。写得更好看
var mylists = new Dictionary<string,List<int>>();
并让编译器弄清楚基于分配的类型是什么。
其次,C#3.0引入了匿名类型。由于匿名类型的定义没有名称,您 需要 如果其类型是匿名的,则能够从初始化表达式中推断出变量的类型。
强调我的。整篇文章, C# 3.0 仍然是静态类型,老实说!, ,以及随后的 系列 都不错。
这是什么 var
是为了.其他用途可能效果不太好。任何与 JScript、VBScript 或动态类型的比较都是无稽之谈。再次注意, var
是 必需的 以便在 .NET 中运行某些其他功能。
我认为 var 的使用应该与明智选择的变量名相结合。
我在 foreach 语句中使用 var 没有问题,只要它不是这样的:
foreach (var c in list) { ... }
如果它更像这样:
foreach (var customer in list) { ... }
...那么阅读代码的人将更有可能理解“列表”是什么。如果您可以控制列表变量本身的名称,那就更好了。
这同样适用于其他情况。这是非常无用的:
var x = SaveFoo(foo);
...但这是有道理的:
var saveSucceeded = SaveFoo(foo);
我想,每个人都有自己的想法。我发现自己正在这样做,这太疯狂了:
var f = (float)3;
我需要某种 12 步 var 程序。我叫马特,我(滥用)使用 var。
我们采用了“为人编写代码,而不是为机器编写代码”的精神,基于这样的假设:您在维护模式上花费的时间比新开发的时间长几倍。
对我来说,这排除了编译器“知道”变量是什么类型的论点 - 当然,你不能第一次编写无效代码,因为编译器会阻止你的代码编译,但是当下一个开发人员正在阅读代码时在 6 个月的时间内,他们需要能够推断出变量的行为正确或错误,并快速找出问题的原因。
因此,
var something = SomeMethod();
我们的编码标准禁止这样做,但我们团队鼓励以下做法,因为它可以提高可读性:
var list = new List<KeyValuePair<string, double>>();
FillList( list );
foreach( var item in list ) {
DoWork( item );
}
这还不错,它更多的是一种风格上的东西,往往是主观的。当您使用 var 和不使用 var 时,它会增加不一致的情况。
另一个值得关注的情况是,在下面的调用中,您无法仅通过查看代码来判断返回的类型 CallMe
:
var variable = CallMe();
这是我对 var 的主要抱怨。
当我在方法中声明匿名委托时,我使用 var ,不知何故 var 看起来比我使用的更干净 Func
. 。考虑这段代码:
var callback = new Func<IntPtr, bool>(delegate(IntPtr hWnd) {
...
});
编辑: :根据 Julian 的输入更新了最后一个代码示例
Var 根本不像变体。该变量仍然是强类型的,只是您不需要按键来获取它。您可以在 Visual Studio 中将鼠标悬停在其上以查看类型。如果您正在阅读打印的代码,您可能需要稍微思考一下才能弄清楚类型是什么。但是只有一行声明它,并且有很多行使用它,因此给事物起合适的名称仍然是使代码更易于理解的最佳方法。
使用 Intellisense 是懒惰的吗?比全名打字要少。或者是否有一些工作量较少但不值得批评的事情?我认为是有的,var 就是其中之一。
最有可能需要的时候是匿名类型(100%需要);但它也避免了琐碎案例的重复,IMO 使界限更加清晰。我不需要为了简单的初始化而两次查看该类型。
例如:
Dictionary<string, List<SomeComplexType<int>>> data = new Dictionary<string, List<SomeComplexType<int>>>();
(请不要编辑上面的 hscroll - 它有点证明了这一点!!!)
对比:
var data = new Dictionary<string, List<SomeComplexType<int>>>();
然而,有时这会产生误导,并可能导致错误。小心使用 var
如果原始变量和初始化类型不相同。例如:
static void DoSomething(IFoo foo) {Console.WriteLine("working happily") }
static void DoSomething(Foo foo) {Console.WriteLine("formatting hard disk...");}
// this working code...
IFoo oldCode = new Foo();
DoSomething(oldCode);
// ...is **very** different to this code
var newCode = new Foo();
DoSomething(newCode);
var 很困难的一种具体情况:离线代码审查,尤其是纸质代码审查。
您不能依靠鼠标悬停来实现这一点。
我不明白有什么大不了的。。
var something = someMethod(); // Type of 'something' not clear <-- not to the compiler!
您仍然对“某事”拥有完整的智能感知,并且对于任何不明确的情况,您都有单元测试,对吧?( 你?)
它不是 varchar,也不是 dim,当然也不是动态类型或弱类型。它正在像这样停止疯狂:
List<somethinglongtypename> v = new List<somethinglongtypename>();
并将总的混乱情况减少到:
var v = new List<somethinglongtypename>();
不错,但不如:
v = List<somethinglongtypename>();
但那就是这样 嘘 是为了.
如果有人正在使用 var
关键字,因为他们不想“弄清楚类型”,这绝对是错误的原因。这 var
关键字不会创建具有动态类型的变量,编译器仍然必须知道该类型。由于变量始终具有特定类型,因此如果可能的话,该类型也应该在代码中显而易见。
使用的充分理由 var
关键字例如:
- 在需要的地方,即声明匿名类型的引用。
- 它使代码更具可读性,即删除重复声明。
写出数据类型通常会使代码更容易理解。它显示了您正在使用的数据类型,这样您就不必通过首先弄清楚代码的作用来弄清楚数据类型。
考虑到智能感知现在有多强大,我不确定 var 是否比类中的成员变量或在可见屏幕区域之外定义的方法中的局部变量更难阅读。
如果您有一行代码,例如
IDictionary<BigClassName, SomeOtherBigClassName> nameDictionary = new Dictionary<BigClassName, SomeOtherBigClassName>();
它比以下内容更容易或更难阅读:
var nameDictionary = new Dictionary<BigClassName, SomeOtherBigClassName>();
我认为VAR的关键是只在适当的情况下使用它,即当在 Linq 中做它所促进的事情时(可能在其他情况下)。
如果你有 得到 某事物的类型,然后你应该使用它 - 不这样做是简单的懒惰(与通常被鼓励的创造性懒惰相反 - 优秀的程序员经常非常努力地懒惰,并且可以被认为是事物的根源首先)。
全面禁止与滥用结构一样糟糕,但确实需要有一个合理的编码标准。
另一件要记住的事情是,它不是 VB 类型 var,因为它不能更改类型 - 它 是 强类型变量只是推断出类型(这就是为什么有人会认为在 foreach 中使用它并非没有道理,但出于可读性和可维护性的原因,我不同意)。
我怀疑这个会一直运行(-:
墨菲
当然, int
很容易,但是当变量的类型是 IEnumerable<MyStupidLongNamedGenericClass<int, string>>
, var 让事情变得更容易。
不幸的是,你和其他人几乎都错了。虽然我同意您的观点,即冗余不是一件好事,但解决此问题的更好方法是执行以下操作:
MyObject m = new();
或者如果您传递参数:
Person p = new("名字", "姓氏);
在创建新对象时,编译器从左侧而不是右侧推断类型。与“var”相比,它还有其他优点,因为它也可以在字段声明中使用(还有一些其他领域它也可能有用,但我不会在这里讨论)。
归根结底,这并不是为了减少冗余。不要误会我的意思,“var”在 C# 中对于匿名类型/投影非常重要,但这里的使用就很遥远了(我已经说了很长一段时间了),因为你混淆了类型正在使用中。必须输入两次的情况太多了,但声明为零次就太少了。
尼古拉斯·帕尔迪诺 .NET/C# MVP 于 2008 年 6 月 20 日上午 08:00
我想,如果您主要关心的是必须减少输入,那么就没有任何争论会影响您使用它。
如果你只是要去 曾经 成为查看你代码的人,那么谁在乎呢?否则,在这样的情况下:
var people = Managers.People
没关系,但在这样的情况下:
var fc = Factory.Run();
它短路了我的大脑可以从代码的“英语”开始形成的任何直接类型推论。
否则,只需对可能需要参与您的项目的其他人使用您的最佳判断和编程“礼貌”即可。
使用 var
而不是显式类型使重构变得更加容易(因此我必须反驳之前的海报,他们的意思是它没有区别或者纯粹是“语法糖”)。
您可以更改方法的返回类型,而无需更改调用该方法的每个文件。想象
...
List<MyClass> SomeMethod() { ... }
...
其用法如下
...
IList<MyClass> list = obj.SomeMethod();
foreach (MyClass c in list)
System.Console.WriteLine(c.ToString());
...
如果你想重构 SomeMethod()
返回一个 IEnumerable<MySecondClass>
, ,您必须更改变量声明(也在 foreach
)在您使用该方法的每个地方。
如果你写
...
var list = obj.SomeMethod();
foreach (var element in list)
System.Console.WriteLine(element.ToString());
...
相反,您不必更改它。
@库:一个例子是代码审查。另一个例子是重构场景。
基本上我不想用鼠标进行类型搜索。它可能不可用。
这是一个品味问题。所有这些大惊小怪的 类型 当您习惯了动态类型语言时,变量的含义就会消失。那是, 如果 你曾经开始喜欢它们(我不确定是否每个人都能,但我确实如此)。
CS var
很酷,因为它 看起来 就像动态类型一样,但实际上是 静止的 键入 - 编译器强制执行正确的用法。
变量的类型实际上并不那么重要(这之前已经说过)。从上下文(它与其他变量和方法的交互)和它的名称中应该相对清楚 - 不要指望 客户名单 包含一个 int
...
我还在等着看我的老板对这件事的看法 - 我得到了一个全面的“继续”,可以在 3.5 中使用任何新的构造,但是我们将如何进行维护呢?
在你之间的比较中 IEnumerable<int>
和 IEnumerable<double>
你不必担心 - 如果你传递了错误的类型,你的代码无论如何都不会编译。
无需担心类型安全,因为 var
是 不是 动态的。这只是编译器的魔法,你所做的任何类型不安全的调用都会被捕获。
Var
Linq 绝对需要:
var anonEnumeration =
from post in AllPosts()
where post.Date > oldDate
let author = GetAuthor( post.AuthorId )
select new {
PostName = post.Name,
post.Date,
AuthorName = author.Name
};
现在看看 匿名枚举 在智能感知中,它会看起来像 IEnumerable<'a>
foreach( var item in anonEnumeration )
{
//VS knows the type
item.PostName; //you'll get intellisense here
//you still have type safety
item.ItemId; //will throw a compiler exception
}
C# 编译器非常聪明 - 如果属性匹配,单独生成的匿名类型将具有相同的生成类型。
除此之外,只要你有智能感知,使用它就很有意义 var
上下文清晰的任何地方。
//less typing, this is good
var myList = new List<UnreasonablyLongClassName>();
//also good - I can't be mistaken on type
var anotherList = GetAllOfSomeItem();
//but not here - probably best to leave single value types declared
var decimalNum = 123.456m;
我想这取决于你的观点。我个人在理解一段代码时从来没有遇到过任何困难,因为 var
“滥用”,我和我的同事经常使用它。(我同意智能感知在这方面是一个巨大的帮助。)我欢迎它作为消除重复性缺陷的一种方式。
毕竟,如果语句像
var index = 5; // this is supposed to be bad
var firstEligibleObject = FetchSomething(); // oh no what type is it
// i am going to die if i don't know
真的很难处理,没有人会使用动态类型语言。
我只在很清楚使用什么类型时才使用 var。
例如,在这种情况下我会使用 var,因为您可以立即看到 x 的类型为“MyClass”:
var x = new MyClass();
在这种情况下我不会使用 var,因为您必须将鼠标拖动到代码上并查看工具提示以查看 MyFunction 返回的类型:
var x = MyClass.MyFunction();
尤其是我 绝不 如果右侧甚至不是方法而只是一个值,请使用 var:
var x = 5;
(因为编译器无法知道我是否想要一个字节、短整型、整数或其他什么)
对我来说,反感 var
说明了为什么 .NET 中的双语很重要。对于那些也做过 VB .NET 的 C# 程序员来说, var
直观上是显而易见的。标准 C# 声明:
List<string> whatever = new List<string>();
在 VB .NET 中,相当于键入以下内容:
Dim whatever As List(Of String) = New List(Of String)
不过,没有人在 VB .NET 中这样做。这是愚蠢的,因为自从 .NET 的第一个版本以来,你就可以做到这一点......
Dim whatever As New List(Of String)
...它创建变量并在一个相当紧凑的行中将其全部初始化。啊,但是如果你想要一个 IList<string>
, ,不是一个 List<string>
?那么,在 VB .NET 中,这意味着您必须执行以下操作:
Dim whatever As IList(Of String) = New List(Of String)
就像你必须在 C# 中做的那样,显然不能使用 var
为了:
IList<string> whatever = new List<string>();
如果你 需要 类型是不同的,它可以是。但良好编程的基本原则之一是减少冗余,而这正是 var 所做的。
将其用于匿名类型 - 这就是它的用途。其他任何东西都太过分了。像许多使用 C 语言长大的人一样,我习惯于查看类型声明的左侧。除非必要,否则我不会看右侧。使用 var
因为任何旧的声明都会让我一直这样做,我个人觉得这很不舒服。
那些说“没关系,用你满意的”的人并没有看到全局。每个人都会在某个时刻拿起其他人的代码,并且必须处理他们在编写代码时做出的任何决定。不得不处理完全不同的命名约定,或者 - 经典的抱怨 - 支撑样式,而不添加整个 'var
或不’的事情混合在一起。最糟糕的情况是一名程序员没有使用 var
然后出现了一位喜欢它的维护者,并使用它扩展了代码。所以现在你的处境一团糟。
标准是一件好事,因为它们意味着您更有可能获得随机代码并能够快速理解它。不同的东西越多,就越难。转向“var Everywhere”风格使得 大的 不同之处。
我不介意动态类型,也不介意隐式类型——使用专门为它们设计的语言。我非常喜欢Python。但 C# 被设计为一种静态显式类型语言,而且它应该保持这样的状态。违反匿名类型的规则已经够糟糕的了;让人们更进一步并进一步打破语言的习语是我不满意的事情。既然精灵已经从瓶子里出来了,它就永远不会再进去了。C# 将分成不同的阵营。不好。
在测试过程中,我多次发现自己有这样的代码:
var something = myObject.SomeProperty.SomeOtherThing.CallMethod();
Console.WriteLine(something);
现在,有时,我想查看 SomeOtherThing 本身包含的内容,SomeOtherThing 与 CallMethod() 返回的类型不同。因为我使用的是 var,所以我只是改变这个:
var something = myObject.SomeProperty.SomeOtherThing.CallMethod();
对此:
var something = myObject.SomeProperty.SomeOtherThing;
如果没有 var,我还必须不断更改左侧声明的类型。我知道这很小,但非常方便。
对于那些认为 var
节省时间,打字所需的击键次数更少:
StringBuilder sb = new StringBuilder();
比
var sb = new StringBuilder();
如果你不相信我,你就数数吧……
19 对 21
如果有必要的话我会解释一下,但只要尝试一下......(根据您的智能感知的当前状态,您可能需要为每个输入多输入几个)
对于您能想到的每种类型都是如此!
我个人的感觉是,除非类型未知,否则永远不应该使用 var,因为它会降低代码中的识别可读性。大脑识别类型所需的时间比识别整行的时间要长。了解机器代码和位的老前辈们确切地知道我在说什么。大脑并行处理,当您使用 var 时,您会强制它序列化其输入。为什么有人想让他们的大脑更加努力地工作?这就是计算机的用途。
我将 var 分割到各个地方,对我来说唯一有问题的地方是内部短类型,例如我更喜欢 int i = 3;
超过 var i = 3;
从我昨天编写的代码来看,它肯定可以使事情变得更简单:
var content = new Queue<Pair<Regex, Func<string, bool>>>();
...
foreach (var entry in content) { ... }
如果没有的话,这将非常冗长 var
.
附录:花一点时间学习一门语言 真实的 类型推断(例如F#) 将展示编译器在正确获取表达式类型方面的能力。这当然意味着我倾向于使用 var
尽我所能,并且现在使用显式类型表明该变量不是初始化表达式的类型。
没有,只是您不必将类型名称写两次。 http://msdn.microsoft.com/en-us/library/bb383973.aspx