前几天我一直在和我的同事聊天,听说他们的编码标准明确禁止他们var
在 C# 中使用关键字。他们不知道为什么会这样,而且我总是发现隐式声明在编码时非常有用。我从来没有遇到任何问题找出变量是什么类型(你只需将鼠标悬停在 VS 中的变量上,你就会以这种方式获得类型)。
有谁知道为什么在 C# 中使用 var 关键字是个坏主意?
前几天我一直在和我的同事聊天,听说他们的编码标准明确禁止他们var
在 C# 中使用关键字。他们不知道为什么会这样,而且我总是发现隐式声明在编码时非常有用。我从来没有遇到任何问题找出变量是什么类型(你只需将鼠标悬停在 VS 中的变量上,你就会以这种方式获得类型)。
有谁知道为什么在 C# 中使用 var 关键字是个坏主意?
2008 年 11 月出版的.Net Framework Design Guidelines(很棒的书)的作者建议var
在类型明显且明确时考虑使用。
另一方面,如果使用var
会导致阅读代码时出现歧义,正如 Anton Gogolev 所指出的,那么最好不要使用它。
在书中(附件A),他们实际上给出了这个例子:
var names = new List<string>(); // good usage of var
string source = GetSource();
var tokens = source.Split(' '); // ok; most developers know String.Split
var id = GetId(); // Probably not good; it's not clear what the type of id is
有可能,为了确保可读性不受低级开发人员的一时兴起,您的组织认为您不值得var
并禁止它。
可惜,这就像拥有一个不错的工具供您使用,但将其保存在上锁的玻璃柜中。
在大多数情况下,使用var
简单类型实际上有助于提高可读性,我们不能忘记使用var
.
var q = GetQValue();
确实是一件坏事。然而,
var persistenceManager = ServiceLocator.Resolve<IPersistenceManager>();
对我来说很好。
底线是:使用描述性标识符名称,你会相处得很好。
作为旁注:我想知道当不允许使用var
关键字时他们如何处理匿名类型。或者他们完全不使用它们?
在大多数情况下,当合理使用(即类型和值相同的简单类型初始化器)时,就可以了。
有时不清楚您是否通过更改它来破坏事物 - 主要是当初始化类型和(原始)变量类型不同时,因为:
在这些情况下,您可能会遇到任何类型解析的问题 - 例如:
在这种情况下,您可以更改代码的含义,并执行不同的操作。那么这是一件坏事。
例子:
隐式转换:
static void Main() {
long x = 17;
Foo(x);
var y = 17;
Foo(y); // boom
}
static void Foo(long value)
{ Console.WriteLine(value); }
static void Foo(int value) {
throw new NotImplementedException(); }
方法隐藏:
static void Main() {
Foo x = new Bar();
x.Go();
var y = new Bar();
y.Go(); // boom
}
class Foo {
public void Go() { Console.WriteLine("Hi"); }
}
class Bar : Foo {
public new void Go() { throw new NotImplementedException(); }
}
ETC
这当然是一个错误。这是因为有些人没有意识到它实际上是强类型的,根本不像 VB 中的 var。
并非所有的公司编码标准都有意义,我曾经为一家公司工作,该公司想在所有类名前加上公司名称。当公司更名时,进行了大规模的返工。
首先,作为一般规则,编码标准应该得到团队的讨论和同意,并且应该写下它们背后的原因,这样任何人都可以知道它们为什么存在。他们不应该是一位大师的神圣真理。
其次,这条规则可能是合理的,因为代码的读取次数多于写入次数。var
加快写作速度,但可能会减慢阅读速度。这显然不是像“始终初始化变量”这样的代码行为规则,因为这两种选择(写入var
和写入类型)具有完全相同的行为。所以这不是一个关键规则。我不会禁止var
,我只会使用“Prefer...”
几个月前,我写了一篇关于这个主题的博客文章。对我来说,我尽可能使用它,并专门围绕类型推断设计我的 API。我使用类型推断的基本原因是
http://blogs.msdn.com/jaredpar/archive/2008/09/09/when-to-use-type-inference.aspx
var
是最新的“如何布置你的牙套”/匈牙利符号/骆驼套管辩论。没有正确的答案,但有些人站在极端。
你的朋友很不幸,他们在其中一名极端分子手下工作。
完全禁止它意味着禁止使用匿名类型(当您更多地使用 LINQ 时,它变得非常有用)。
这是简单明了的愚蠢,除非有人可以正式提出一个从不使用匿名类型的充分理由。
用简单的英语理解“var”
我将向您展示使用 AND 不使用 'var' 是为了清楚地沟通。
我将展示使用 'var' 使代码更易于阅读的示例,以及使用 var 使事情难以理解的其他示例。
不仅如此,您还会看到“var”的清晰程度在很大程度上取决于您在代码中为其他所有内容命名的内容。
举个例子:
杰克向比尔打招呼。他不喜欢他,所以他转身往另一条路走。
谁走的另一条路?杰克还是比尔?在这种情况下,“Jake”和“Bill”就像类型名称。而“He”和“him”就像 var 关键字一样。在这种情况下,更具体可能会有所帮助。下面的例子就更清楚了。
杰克向比尔打招呼。杰克不喜欢比尔,所以他转身走另一条路。
在这种情况下,更具体使句子更清晰。但情况并非总是如此。在某些情况下,具体化会使阅读变得更加困难。
比尔喜欢书,所以比尔去了图书馆,比尔拿出一本比尔一直很喜欢的书。
在这种情况下,如果我们使用“他”并且在某些情况下将他的名字全部省略掉,这将更容易阅读句子,这相当于使用var
关键字。
比尔喜欢书,所以他去图书馆拿出一本他一直很喜欢的书。
这些类比涵盖了要点,但并不能说明全部。看到在这些例子中,只有一种方式来指代这个人。要么用他们的名字,例如比尔,要么用更笼统的方式,如“他”和“他”。但我们只使用一个词。
在代码的情况下,您有两个“词”,类型和变量名。
Person p = GetPerson();
现在的问题变成了那里是否有足够的信息让您轻松确定p
是什么?您是否仍然知道在这种情况下人们是什么:
var p = GetPerson();
这个怎么样:
var p = Get();
这个怎么样:
var person = Get();
或者这个:
var t = GetPerson();
或者这个:
var u = Person.Get();
关键字var
在给定场景中是否有效在很大程度上取决于代码的上下文,例如变量、类和方法的名称,以及代码的复杂性。
我个人喜欢使用对我来说var
更全面的关键字。但我也倾向于以类型命名我的变量,所以我并没有真正丢失任何信息。
也就是说,有时我会例外,这就是任何复杂事物的本质,软件如果不复杂就什么都不是。
如果使用不当,可能会损害可读性。但是完全禁止它有点奇怪,因为如果没有它,您的同事将很难使用匿名类型。
您可能会认为 Microsoft 的意见是相关的,因为 C# 是他们的语言:
“但是,使用var至少有可能使其他开发人员更难以理解您的代码。因此,C# 文档通常仅在需要时才使用var 。”
请参阅MSDN - Implicitly Typed Local Variables (C# Programming Guide)的最后一段。
您还应该知道var删除了初始分配上的编译时数据类型测试。
var x = "mistake"; // error not found by compiler
int x = "mistake"; // error found
由于大多数变量只分配一次,因此一致使用var几乎消除了对变量分配的所有数据类型测试。
这使您的代码容易受到意外更改的影响,例如合并工具或疲倦的开发人员所做的更改。
这确实是您的代码的可读性问题。
我个人的偏好是只对匿名类型使用“var”(实际上,如果您想使用匿名类型,则需要使用 var),而这些大多来自 LINQ 查询。在这些情况下,如果您的查询投射到新的(隐式和匿名)类型,您别无选择,只能使用 var。
然而,C# 3.0 很乐意让你在任何你喜欢的地方使用 var,在 LINQ 和匿名类型之外,例如:
var myint = 0;
var mystring = "";
是完全有效的,并且 myint 和 mystring 将被用于初始化它们的推断值强类型化。(因此,myint 是 System.Int32 而 mystring 是 System.String)。当然,在查看用于初始化变量的值时,它们将被隐式键入的类型是相当明显的,但是,我认为如果将上面的代码写成这样的话,代码的可读性会更好:
int myint = 0;
string mystring = "";
因为您可以一目了然地看到这些变量的确切类型。
考虑一下这个有点令人困惑的场景:
var aaa = 0;
double bbb = 0;
完全有效的代码(如果有点不合常规的话),但在上面,我知道 bbb 是一个 double,尽管初始化值看起来是一个 int,但 aaa 绝对不会是一个 double,而是一个 int。
来自Department of Declaration Redundancy Department(来自Jeff's Coding Horror):
“无论何时何地,我都会使用隐式变量类型,它使我的代码更简洁。应该积极追求任何从我们的代码中删除冗余的东西——包括切换语言。”
我自己认为这是值得考虑的,但制定一个关于何时使用或不使用的综合指南将是矫枉过正。
隐式输入很棒,而那些完全禁止它的人会损害生产力并邀请脆弱的代码。
它几乎就像类型安全、经过编译器检查的鸭子类型,在重构时非常有用。例如,如果我有一个返回 List 的方法,并且我将它重构为返回 IEnumerable,那么该方法的任何调用者使用 var 关键字并且只使用 IEnumerable 方法都可以。如果我已明确指定,例如,List,那么我必须将其更改为 IEnumerable。
显然,如果任何隐式类型调用者需要 List 方法,那么我在构建时会遇到编译错误,但如果是这种情况,我可能无论如何都不应该更改返回类型。
Eric Lippert总结得很好:
int
我自己的观点:我发现使用,甚至 a之类的类型更难阅读,而且有点毫无string
意义。毕竟它是关于可读性的(除了在 LINQ 中使用它的地方),所以当 vars 被喷溅时,它可能更难阅读,并且违背了语言设计者打算使用的关键字的目的。bool
User
当使用 var 导致类型属于某个基类而不是实际的 DataRow 类型时,我遇到过一些案例(当我通过 Table.Rows 集合进行 foreach 时)。那是我唯一一次遇到 var 问题。
var
以下是我针对显式类型与显式类型的效率进行的测试的结果:
private void btnVar_Click(object sender, EventArgs e)
{
Stopwatch obj = new Stopwatch();
obj.Start();
var test = "Test";
test.GetType();
obj.Stop();
lblResults.Text = obj.Elapsed.ToString();
}
private void btnString_Click(object sender, EventArgs e)
{
Stopwatch obj = new Stopwatch();
obj.Start();
string test = "Test";
obj.Stop();
lblResults.Text = obj.Elapsed.ToString();
}
第一个标签结果是:00:00:00 000034
第二个标签结果是:00:00:00 00008