0x00 TL;DR

var 支持了局部变量的隐式推导, var 的引入支持了匿名类型, 支持了 LINQ.

在可以不写 var 的使用场景下, 写 var 和不写 var 的编译后代码是相同的.

var 简化了代码的书写方式, 让你繁忙搬砖的生活变得更加美好.

0x01 引子

在知乎上看到有题主问

C# 作为一种静态类型语言,为什么会引入 var

我想你应该有自己的答案,

在本篇文章中, 我将努力把自己的看法表达出来,

然后我们可以就文中谈到的一些概念进行更深♂入的讨论.

0x02 解释

首先什么是 var

var 是在 C# 3.0 时引入的一个关键字, 其作用是根据初始值推断变量的类型, 从而简化局部变量的声明.

我们知道, C# 3.0 相对于 C# 2.0, 最重要的就是引入了LINQ, 从2007年 C# 3.0 发布到目前为止, LINQ在各个方向大放光明, 并且在不断创新, 比如 PLINQ (Parallel LINQ)和 Rx(Reactive Extensions).

那么LINQ 是怎么样被创造出来的, 底层使用了哪些技术, 这些问题并不难解答, 以下是部分黑科技列表:

  • 自动属性(Auto Property)
  • 隐式类型的局部变量(implicit typed local variable)
  • 对象和集合初始化程序(Object/Collection initializer)
  • 隐式类型数组(implicit typed array)
  • 匿名类型(anonymous type)

可以看出几乎 C# 3.0 的每一个特性都是为了实现LINQ而引入的. 其中, 隐式类型推导匿名类型 特性相互搞基, 两者缺一不可.

再来说说静态

我们知道 C# 1.0 的类型系统是静态、显式和安全的。

引入了泛型之后, C# 2.0 仍然是静态、显式和安全的。

在 C# 3.0 中, “静态"和"安全"仍然是成立的, 现在你可以要求编译器帮你推断局部变量的类型, 所以, 在大多数情况下, 它是隐式类型的, 当然, 有些情况你还是需要帮助编译器显式指定类型.

理解"静态"这一点很重要, 有些开发者对var感到恐惧, 认为var使 C# 变成了动态类型或弱类型的语言, 我们知道,

恐惧来源于未知 恐惧来源于未知

下面两段代码

1
MyType a = new MyType();

1
var a = new MyType();

的最终编译结果是一样的.

ILspy 来看下生成的 IL 代码:

1
//假装这里有 IL 代码, 你们配合一下

可以看到变量仍然是静态类型的, 你只是没把他的类型显式写出来而已.

如果你在下面搞事情, 写出了这样的代码

1
2
3
var a = new MyType();
...
a = 0;  //x 这里是编译不通过的

这就是静态类型带来的好处,

说到这里我们应该知道, 一个语言是不是静态类型的, 究竟应该怎么来判断?

区别就是他的类型是在编译时检查的, 还是在运行时检查的.

所以在 C# 4.0 引入了 dynamic 后, 我们不能再说 C# 完全是静态类型语言.

对比

Javascript var

//todo

C++ auto

我不会 C++, 请[哔]大的说下 C++ 最新加入的 auto 关键字和 var 有什么异同?

那么哪里才可以买得到呢?

之所以使用隐式类型, 主要原因是它减少了代码量, 这就意味着可读性增强. 举个栗子你感受一下

栗子1:

1
Dictionary<string,List<Person>> groupedPerson = new Dictionary<string,List<Person>>();

栗子2:

1
var groupedPerson = new Dictionary<string,List<Person>>();

使用 var 改变了这行代码的重心, 你的同事可以将注意力放到确切的类型上.

你也可以 var 一个 lambda 表达式, 你的同事可以不用知道这个 lambda 表达式的类型是什么, 他只需要关心函数的逻辑.

只要还能嘎嘎叫, 我是鸭子我骄傲

并且, 最重要的, var 提供了对 匿名类型 的支持.

那么问题来了, “你把 var 说的天花乱坠, 是不是我在所有地方都应该弃用显式类型, 改用 var 进行声明呢? "

从小学做判断题我们都知道, 凡是太绝对的用词都打x, 比如所有. 比如凡是.

什么时候适合使用隐式类型, 也是可以引起圣战的一个问题.

为了使用 C# 3.0 的另一个特性, 匿名类型, 你必须要使用 var 来进行声明, 这是毋庸讨论的.

那么是在任何地方都用呢, 还是在任何地方都不用呢(我不使用匿名类型, 再问自杀!)?

可读性是一个重要的影响因素, var 可以提升可读性, var 也可以降低可读性, 这听起来有些打自己的脸, 但是事实如此.

一个降低可读性的场景:

假如不显示地指出要声明的变量是什么类型, 只通过读代码的方式, 有时候可能不好确定它的类型.

注意这里我们使用了有时候可能不好确定这一串车轱辘话, 如果你能说出这一段话脸不红心不跳,

说明你有成为架构师的潜力.

因为合格的架构师, 经常挂在嘴边的话就是:

It depends

Jon Skeet 一些关于 var 的建议

  • 如果让读代码的人一眼就能看出变量的类型是很重要的,就使用显式类型。
  • 如果变量直接用一个构造函数初始化,而且类型名称很长(用泛型时经常会这样) ,就考 虑使用隐式类型。
  • 如果变量的确切类型不重要,而且它的本质在当前上下文中已很清楚,就用隐式类型, 从而不去强调代码具体是如何达到其目标的,而是关注它想要达到什么目标这个更高级 别的主题。
  • 在开发一个新项目的时候,与团队成员就这件事情进行商议。
  • 如有疑虑,一行代码用两种方式都写一写,根据直觉选一个最“顺眼”的。

0x03 脑洞

RPC 静态调用分析

A story

作为服务消费者, 我希望在写完代码就可以知道自己的 RPC 调用是否存在问题, 如果服务不存在, 我希望立刻知道结果, 而不是部署到测试服务器或者生产环境中才知道调用失败.

或者作为服务提供方. 我的某个版本的服务要下线了, 我希望能够看到是否有人在调用这个版本, 具体都有哪些人在调用, 这样我可以预留时间并告知他们及时升级, 从而能够做到安全下线.

解决方案

  • 服务的消费者在编译时可以知道自己调用的服务是否存在
  • 服务提供方在下线某些服务前可以知道自己的服务是否有客户端在调用.

0x04 Reference