服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - c# 闭包的相关知识以及需要注意的地方

c# 闭包的相关知识以及需要注意的地方

2022-09-15 17:53老胡写代码 C#

这篇文章主要介绍了c# 闭包的相关知识以及需要注意的地方,文中讲解非常细致,代码帮助大家理解和学习,感兴趣的朋友可以参考下

虽然闭包主要是函数式编程的玩意儿,而C#的最主要特征是面向对象,但是利用委托或lambda表达式,C#也可以写出具有函数式编程风味的代码。同样的,使用委托或者lambda表达式,也可以在C#中使用闭包。

根据WIKI的定义,闭包又称语法闭包或函数闭包,是在函数式编程语言中实现语法绑定的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。闭包也可以延迟变量的生存周期。

嗯。。看定义好像有点迷糊,让我们看看下面的例子吧

?
1
2
3
4
5
6
7
8
9
10
11
12
13
class Program
{
  static Action CreateGreeting(string message)
  {
    return () => { Console.WriteLine("Hello " + message); };
  }
 
  static void Main()
  {
    Action action = CreateGreeting("DeathArthas");
    action();
  }
}

这个例子非常简单,用lambda表达式创建一个Action对象,之后再调用这个Action对象。
但是仔细观察会发现,当Action对象被调用的时候,CreateGreeting方法已经返回了,作为它的实参的message应该已经被销毁了,那么为什么我们在调用Action对象的时候,还是能够得到正确的结果呢? 

原来奥秘就在于,这里形成了闭包。虽然CreateGreeting已经返回了,但是它的局部变量被返回的lambda表达式所捕获,延迟了其生命周期。怎么样,这样再回头看闭包定义,是不是更清楚了一些?

闭包就是这么简单,其实我们经常都在使用,只是有时候我们都不自知而已。比如大家肯定都写过类似下面的代码。

?
1
2
3
4
5
6
7
void AddControlClickLogger(Control control, string message)
{
    control.Click += delegate
    {
        Console.WriteLine("Control clicked: {0}", message);
    }
}

这里的代码其实就用了闭包,因为我们可以肯定,在control被点击的时候,这个message早就超过了它的声明周期。合理使用闭包,可以确保我们写出在空间和时间上面解耦的委托。

不过在使用闭包的时候,要注意一个陷阱。因为闭包会延迟局部变量的生命周期,在某些情况下程序产生的结果会和预想的不一样。让我们看看下面的例子。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
  static List<Action> CreateActions()
  {
    var result = new List<Action>();
    for(int i = 0; i < 5; i++)
    {
      result.Add(() => Console.WriteLine(i));
    }
    return result;
  }
 
  static void Main()
  {
    var actions = CreateActions();
    for(int i = 0;i<actions.Count;i++)
    {
      actions[i]();
    }
  }
}

这个例子也非常简单,创建一个Action链表并依次执行它们。看看结果

c# 闭包的相关知识以及需要注意的地方

c# 闭包的相关知识以及需要注意的地方

相信很多人看到这个结果的表情是这样的!!难道不应该是0,1,2,3,4吗?出了什么问题?

刨根问底,这儿的问题还是出现在闭包的本质上面,作为“闭包延迟了变量的生命周期”这个硬币的另外一面,是一个变量可能在不经意间被多个闭包所引用。

在这个例子里面,局部变量i同时被5个闭包引用,这5个闭包共享i,所以最后他们打印出来的值是一样的,都是i最后退出循环时候的值5。

要想解决这个问题也很简单,多声明一个局部变量,让各个闭包引用自己的局部变量就可以了。

?
1
2
3
4
5
6
7
8
9
10
11
//其他都保持与之前一致
static List<Action> CreateActions()
{
  var result = new List<Action>();
  for (int i = 0; i < 5; i++)
  {
    int temp = i; //添加局部变量
    result.Add(() => Console.WriteLine(temp));
  }
  return result;
}

#7bd4eabb8d859212446baf425956f97c#

这样各个闭包引用不同的局部变量,刚刚的问题就解决了。

除此之外,还有一个修复的方法,在创建闭包的时候,使用foreach而不是for。至少在C# 7.0 的版本上面,这个问题已经被注意到了,使用foreach的时候编译器会自动生成代码绕过这个闭包陷阱。

?
1
2
3
4
5
6
7
8
9
10
//这样fix也是可以的
static List<Action> CreateActions()
{
  var result = new List<Action>();
  foreach (var i in Enumerable.Range(0,5))
  {
    result.Add(() => Console.WriteLine(i));
  }
  return result;
}

这就是在闭包在C#中的使用和其使用中的一个小陷阱,希望大家能通过老胡的文章了解到这个知识点并且在开发中少走弯路!

以上就是c# 闭包的相关知识以及需要注意的地方的详细内容,更多关于c# 闭包的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/deatharthas/p/13166987.html

延伸 · 阅读

精彩推荐
  • C#C#实现学员信息管理系统

    C#实现学员信息管理系统

    这篇文章主要为大家详细介绍了C#实现学员信息管理系统,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    zxh...3912022-07-27
  • C#Unity实现见缝插针小游戏

    Unity实现见缝插针小游戏

    这篇文章主要为大家详细介绍了Unity实现见缝插针小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    菠萝小笨笨8482022-09-02
  • C#浅谈c# 浮点数计算

    浅谈c# 浮点数计算

    本文通过具体的示例给大家演示了下C#中浮点数运算所遇到的问题及解决方法,有需要的小伙伴可以参考下...

    大稳·杨10012022-01-22
  • C#C# Winform 子窗体访问父级窗体的控件和属性

    C# Winform 子窗体访问父级窗体的控件和属性

    本文主要介绍两种子窗体访问父窗体控件和属性的方法,大家可以参考一下,本人比较偏向第二种,把父窗体作为属性传递,一劳永逸,想访问父窗体的什...

    xch_yang10052021-11-19
  • C#Unity实现游戏存档框架

    Unity实现游戏存档框架

    这篇文章主要为大家详细介绍了Unity实现游戏存档框架,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    hackerzhuli9022022-08-17
  • C#C#使用浏览按钮获得文件路径和文件夹路径的方法

    C#使用浏览按钮获得文件路径和文件夹路径的方法

    这篇文章主要介绍了C#使用浏览按钮获得文件路径和文件夹路径的方法,结合实例形式分析了C#浏览器事件响应及文件操作相关技巧,需要的朋友可以参考下...

    roucheng6982022-01-05
  • C#SuperSocket入门--Telnet服务器和客户端请求处理

    SuperSocket入门--Telnet服务器和客户端请求处理

    本文的控制台项目是根据SuperSocket官方Telnet示例代码进行调试的,官方示例代码:Telnet示例。下面跟着小编一起来看下吧...

    黄昏前黎明后10592021-12-21
  • C#C#无边框窗体实现以及拖动代码

    C#无边框窗体实现以及拖动代码

    我们给大家分享了关于C#无边框窗体实现以及拖动代码,大家在程序设计的时候如果用的到一起跟着小编学习下吧。...

    C#教程网5712022-02-21