聊聊 C# 中 using 语句可能的 3 个陷阱

简介: 大家都知道,C# 中可以用 using 关键字来简化非托管资源(如文件流、数据库连接等)的释放,但是如果用在错误的使用场景,可能会带来一些意想不到又难以排查的问题,来看看有哪些可能的陷阱吧!

Coding-74.png

前言

大家都知道,C# 中可以用 using 关键字来简化非托管资源(如文件流、数据库连接等)的释放,当变量离开 using 作用的范围后,会自动调用对象的 Dispose 方法,从而完成非托管资源的释放。在 C#8.0,进一步引入了简化版的 "using声明" 语法来避免多个 using 语句的嵌套,保证代码的优美,例如:

string connStr = "......";

using var conn = new SqlConnection(connStr);

conn.Open();

using var cmd = conn.CreateCommand();

cmd.CommandText = "select * from testdb";

using var reader = cmd.ExecuteReader();

while (reader.Read())
{
   
    // ......
}

虽然 using 语句非常有用,但在实际使用过程中也存在一些潜在的问题,不可不察!

可能的陷阱

  1. 嵌套使用 using 语句

    当多个 using 语句嵌套在一起,内部 using 语句中的资源在释放时,可能会把外部 using 语句中的资源也释放掉,比如:

     using (Stream stream = new FileStream("d:\1.txt", FileMode.OpenOrCreate))
     {
         
         using (StreamWriter writer = new StreamWriter(stream))
         {
         
             // ......
         }
     }
    

    例子中当内部的 writer 释放时,会同时释放外部的 stream 对象,这是因为 StreamWriter 类型 Dispose 机制所造成,所以当我们不太清除内层对象是否与外层对象有关系时,尤其要慎用 using 语句。

    除此之外,外部的 using 有时候也可能会在内部的 using 结束前就释放资源,导致意外的问题发生,比如数据库连接。

  2. 资源释放顺序:

    当多个 using 语句嵌套在一起时,如果多个资源需要按照特定顺序释放时,using 语句可能无法保证这一顺序,导致意外的问题发生。

  3. 作用域:

    简化版的 "using声明" 语法默认的作用域是整个方法体,所以很容易导致意外的问题发生,比如以下代码:

     void usingTest() 
     {
         
         using var outStream = File.OpenWrite("d:/1.txt");
         using var writer = new StreamWriter(outStream);
         writer.WriteLine("Hello world");
         string s = File.ReadAllText("d:/1.txt");
         Console.WriteLine(s);
     }
    

    当代码执行到下面这行代码时,就会提示文件被占用的错误。

     string s = File.ReadAllText("d:/1.txt");
    

    需要记得用花括号 {}using 语句包起来才能避免这个问题,如:

     void usingTest() 
     {
         
         {
         
             using var outStream = File.OpenWrite("d:/1.txt");
             using var writer = new StreamWriter(outStream);
             writer.WriteLine("Hello world");
         }
         string s = File.ReadAllText("d:/1.txt");
         Console.WriteLine(s);
     }
    

总结

using 语句本质上是 try-finally 的语法糖,所以当多个 using 语句嵌套在一起的时候,实际上就是多个 try-finally 语句嵌套在一起,所以造成一些奇怪的问题也就不奇怪了,尤其是 C#8.0 进一步简化 using 语句之后。

我们在享受 using 语句带来的便利的同时,也要注意 using 语句正确的使用场景,尤其是在需要使用嵌套 using 语句的时候,这样才能提高程序的健壮性。

最后,using 语句除了用于释放非托管资源之外,还在其它的用途,比如引用命名空间、为命名空间或类型创建别名等,有兴趣的童鞋可以继续深入了解。

我是老杨,一个执着于编程乐趣、至今奋斗在一线的 10年+ 资深研发老鸟,是软件项目管理师,也是快乐的程序猿,持续免费分享全栈实用编程技巧、项目管理经验和职场成长心得。喜欢文章欢迎关注老杨的公众号,和你共同探索代码世界的奥秘!

相关文章
|
4月前
|
C++
C++ 条件与 If 语句:掌握逻辑判断与流程控制精髓
C++ 中的条件语句用于根据布尔表达式的真假执行不同代码。`if` 用于当条件为真时执行一段代码,`else` 配合 `if` 在条件不成立时执行另一段代码。`else if` 允许测试额外的条件。`switch` 语句提供多分支选择。还有三元运算符 `(condition) ? expressionTrue : expressionFalse`,它是一种简写的 if...else 形式,常用于一行内作出决定。
67 0
|
10月前
|
编译器 C语言
C语言编程陷阱:语法陷阱
c语言要求在函数调用时即使函数不带参数,也应该包括函数列表。 是挂else问题
46 0
|
10月前
|
自然语言处理 编译器 C语言
C语言编程陷阱:词法陷阱
推荐一个零声学院免费教程,个人觉得老师讲得不错, 服务器课程
36 0
|
1月前
|
Java
在Java编程的广阔天地中,条件语句是控制程序流程、实现逻辑判断的重要工具。
在Java编程中,if-else与switch作为核心条件语句,各具特色。if-else以其高度灵活性,适用于复杂逻辑判断,支持多种条件组合;而switch在多分支选择上表现优异,尤其适合处理枚举类型或固定选项集,通过内部跳转表提高执行效率。两者各有千秋:if-else擅长复杂逻辑,switch则在多分支选择中更胜一筹。理解它们的特点并在合适场景下使用,能够编写出更高效、易读的Java代码。
29 1
|
3月前
|
Java C++ Python
选择、条件、循环语句是编程语言中用于控制程序流程的重要语句。
选择、条件、循环语句是编程语言中用于控制程序流程的重要语句。
|
4月前
|
程序员 C++
C++语言中的控制流语句
C++语言中的控制流语句
|
4月前
|
存储 程序员 编译器
C陷阱与缺陷:语法陷阱
C陷阱与缺陷:语法陷阱
36 0
|
4月前
|
自然语言处理 编译器 程序员
C陷阱与缺陷:词法陷阱
C陷阱与缺陷:词法陷阱
37 0
|
编译器 C语言
《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题
《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题
120 0
|
程序员 C语言
C语言编程—判断语句
判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。 C 语言把任何非零和非空的值假定为 true,把零或 null 假定为 false。
303 0