C#3.0新增功能03 隐式类型本地变量
var i = 10; // 隐式类型 int i = 10; // 显式类型
下面的示例演示两个查询表达式。 在第一个表达式中,var
的使用是允许的,但不是必需的,因为查询结果的类型可以明确表述为 IEnumerable<string>
。 不过,在第二个表达式中,var
允许结果是一系列匿名类型,且相应类型的名称只可供编译器本身访问。 如果使用 var
,便无法为结果新建类。 请注意,在示例 #2 中,foreach
迭代变量 item
必须也为隐式类型。
// 示例 #1: 当 select 子句指定字符串时,var是可选的 string[] words = { "apple", "strawberry", "grape", "peach", "banana" }; var wordQuery = from word in words where word[0] == 'g' select word; // 因为序列中的每个元素都是字符串,而不是匿名类型,所以var在这里也是可选的。 foreach (string s in wordQuery) { Console.WriteLine(s); } // 示例 #2: var 是必需的,因为select子句指定匿名类型 var custQuery = from cust in customers where cust.City == "Phoenix" select new { cust.Name, cust.Phone }; // 必须使用var,因为序列中的每个项都是匿名类型 foreach (var item in custQuery) { Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone); }
可声明局部变量而无需提供显式类型。 var
关键字指示编译器通过初始化语句右侧的表达式推断变量的类型。 推断类型可以是内置类型、匿名类型、用户定义类型或 .NET Framework 类库中定义的类型。 有关如何使用 var
初始化数组的详细信息,请参阅隐式类型化数组。
以下示例演示使用 var
声明局部变量的各种方式:
// i 被编译成 int 类型 var i = 5; // s 被编译成 string 类型 var s = "Hello"; // a 被编译成 int[] 数组 var a = new[] { 0, 1, 2 }; // expr 被编译成 IEnumerable<Customer> 或者 IQueryable<Customer> 类型 var expr = from c in customers where c.City == "London" select c; // anon 被编译成匿名类型 var anon = new { Name = "Terry", Age = 34 }; // list 被编译成 List<int> 集合 var list = new List<int>();
重要的是了解 var
关键字并不意味着“变体”,并且并不指示变量是松散类型或是后期绑定。 它只表示由编译器确定并分配最适合的类型。
在以下上下文中,可使用 var
关键字:
-
在局部变量(在方法范围内声明的变量)上,如前面的示例所示。
-
在 for 初始化语句中
for(var x = 1; x < 10; x++)
- 在 foreach 初始化语句中
foreach(var item in list){...}
- 在 using 域间中
using (var file = new StreamReader("C:\\myfile.txt")) {...}
有关详细信息,请参阅如何:在查询表达式中使用隐式类型本地变量和数组。
var 和匿名类型
在许多情况下,使用 var
是可选的,只是一种语法便利。 但是,在使用匿名类型初始化变量时,如果需要在以后访问对象的属性,则必须将变量声明为 var
。 这是 LINQ 查询表达式中的常见方案。 有关详细信息,请参阅匿名类型。
从源代码角度来看,匿名类型没有名称。 因此,如果使用 var
初始化了查询变量,则访问返回对象序列中的属性的唯一方法是在 foreach
语句中将 var
用作迭代变量的类型。
class ImplicitlyTypedLocals2 { static void Main() { string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" }; // 如果查询生成一系列匿名类型,则在foreach语句中使用var访问属性。 var upperLowerWords = from w in words select new { Upper = w.ToUpper(), Lower = w.ToLower() }; // 执行查询 foreach (var ul in upperLowerWords) { Console.WriteLine("Uppercase: {0}, Lowercase: {1}", ul.Upper, ul.Lower); } } } /* 输出: Uppercase: APPLE, Lowercase: apple Uppercase: BLUEBERRY, Lowercase: blueberry Uppercase: CHERRY, Lowercase: cherry */
特别说明
以下限制适用于隐式类型化变量声明:
-
仅当局部变量在相同语句中进行声明和初始化时,才能使用
var
;变量不能初始化为 null,也不能初始化为方法组或匿名函数。 -
var
不能在类范围内对字段使用。 -
使用
var
声明的变量不能在初始化表达式中使用。 换句话说,此表达式是合法的: int i = (i = 20);
,但是此表达式会生成编译时错误:var i = (i = 20);
-
不能在相同语句中初始化多个隐式类型化变量。
-
如果一种名为
var
的类型处于范围内,则var
关键字会解析为该类型名称,不会被视为隐式类型化局部变量声明的一部分。
带 var
关键字的隐式类型只能应用于本地方法范围内的变量。 隐式类型不可用于类字段,因为 C# 编译器在处理代码时会遇到逻辑悖论:编译器需要知道字段的类型,但它在分析赋值表达式前无法确定类型,而表达式在不知道类型的情况下无法进行计算。 考虑下列代码:
private var bookTitles;
bookTitles
是类型为 var
的类字段。 由于该字段没有要计算的表达式,编译器无法推断出 bookTitles
应该是哪种类型。 此外,向该字段添加表达式(就像对本地变量执行的操作一样)也是不够的:
private var bookTitles = new List<string>();
当编译器在代码编译期间遇到字段时,它会在处理与其关联的任何表达式之前记录每个字段的类型。 编译器在尝试分析 bookTitles
时遇到相同的悖论:它需要知道字段的类型,但编译器通常会通过分析表达式来确定 var
的类型,这在事先不知道类型的情况下无法实现。
你可能会发现,对于在其中难以确定查询变量的确切构造类型的查询表达式,var
也可能会十分有用。 这可能会针对分组和排序操作发生。
当变量的特定类型在键盘上键入时很繁琐、或是显而易见、或是不会提高代码的可读性时,var
关键字也可能非常有用。 var
采用此方法提供帮助的一个示例是针对嵌套泛型类型(如用于分组操作的类型)。 在下面的查询中,查询变量的类型是 IEnumerable<IGrouping<string, Student>>
。 只要你和必须维护你的代码的其他人了解这一点,使用隐式类型化实现便利性和简便性时便不会出现问题。
// 与前面的示例相同,只是我们使用整个姓氏作为键。 // 查询变量是IEnumerable<igrouping<string,student>> var studentQuery3 = from student in students group student by student.Last;
但是,使用 var
至少有可能使代码对其他开发人员更加难以理解。 为此,C# 文档通常只在需要时才使用 var
。
其他技术请参阅
- C# 参考
- 隐式类型化数组
- 如何:在查询表达式中使用隐式类型本地变量和数组
- 匿名类型
- 对象和集合初始值设定项
- var
- LINQ 查询表达式
- LINQ(语言集成查询)
- for
- foreach, in
- using 语句
成在管理,败在经验;嬴在选择,输在不学! 贵在坚持!
个人作品
BIMFace.SDK.NET
开源地址:https://gitee.com/NAlps/BIMFace.SDK
系列博客:https://www.cnblogs.com/SavionZhang/p/11424431.html
系列视频:https://www.cnblogs.com/SavionZhang/p/14258393.html
技术栈
1、AI、DeepSeek、MiniMax、通义千问
2、Visual Studio、.NET Core/.NET、MVC、Web API、RESTful API、gRPC、SignalR、Java、Python
3、jQuery、Vue.js、Bootstrap、ElementUI
4、数据库:分库分表、读写分离、SQLServer、MySQL、PostgreSQL、Redis、MongoDB、ElasticSearch、达梦DM、GaussDB、OpenGauss
5、架构:DDD、ABP、SpringBoot、jFinal
6、环境:跨平台、Windows、Linux
7、移动App:Android、IOS、HarmonyOS、微信小程序、钉钉、uni-app、MAUI
8、分布式、高并发、云原生、微服务、Docker、CI/CD、DevOps、K8S;Dapr、RabbitMQ、Kafka、RPC、Elasticsearch
欢迎关注作者头条号 张传宁IT讲堂,获取更多IT文章、视频等优质内容。
出处:www.cnblogs.com/SavionZhang
作者:张传宁 技术顾问、培训讲师、微软MCP、系统架构设计师、系统集成项目管理工程师、科技部创新工程师。
专注于企业级通用开发平台、工作流引擎、自动化项目(代码)生成器、SOA 、DDD、 云原生(Docker、微服务、DevOps、CI/CD);PDF、CAD、BIM 审图等研究与应用。
多次参与电子政务、图书教育、生产制造等企业级大型项目研发与管理工作。
熟悉中小企业软件开发过程:可行调研、需求分析、架构设计、编码测试、实施部署、项目管理。通过技术与管理帮助中小企业实现互联网转型升级全流程解决方案。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如有问题,可以通过邮件905442693@qq.com联系。共同交流、互相学习。
如果您觉得文章对您有帮助,请点击文章右下角【推荐】。您的鼓励是作者持续创作的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?