请选择 进入手机版 | 继续访问电脑版

[C#教程] C# 本地函数与 Lambda 表达式具体 先容

[复制链接]
查看123 | 回复7 | 2021-9-14 10:54:58 | 显示全部楼层 |阅读模式
目次

1、C# 本地函数与 Lambda 表达式

C# 局部函数通常被视为

  1. lambda
复制代码
表达式的进一步加强 。固然 功能是干系 的,但也存在庞大 差异。

  1. Local Functions
复制代码
  1. 嵌套函数
复制代码
]功能的 C# 实现。一种语言在支持 lambdas 之后获得对嵌套函数的支持几个版本是有点不寻常 的。通常环境 相反。

Lambda 或一样寻常 的一流函数必要 实现未在堆栈上分配且生命周期与必要 它们的功能对象干系 联的局部变量。假如 不依赖 垃圾网络 或通过捕获列表等办理 方案将变量全部 权的负担减轻给用户,则几乎不大概 精确 有效 地实现它们。对于某些早期语言来说,这是一个严肃 的壅闭 标题 。嵌套函数的简单实现不会碰到 这种复杂环境 ,因此一种语言更常见的是仅支持嵌套函数而不支持 lambda。

无论怎样 ,由于 C# 长期以来不停 使用 lambda,因此从差异和相似之处来看本地函数确实是故意 义的。

2、Lambda 表达式

Lambda 表达式x => x + x是抽象地表示一段代码以及它怎样 绑定到其词法环境中的参数和变量的表达式。作为代码的抽象表示,lambda 表达式不能单独使用 。为了使用 由 lambda 表达式天生 的值,必要 将其转换为更多内容,比方 委托或表达式树。

  1. using System;
  2. using System.Linq.Expressions;
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. // can't do much with the lambda expression directly
  8. // (x => x + x).ToString(); // error
  9. // can assign to a variable of delegate type and invoke
  10. Func<int, int> f = (x => x + x);
  11. System.Console.WriteLine(f(21)); // prints "42"
  12. // can assign to a variable of expression type and introspect
  13. Expression<Func<int, int>> e = (x => x + x);
  14. System.Console.WriteLine(e); // prints "x => (x + x)"
  15. }
  16. }
复制代码

有几点值得留意 :

    1. lambdas
    复制代码
    是产生函数值的表达式。
    1. lambda
    复制代码
    值的生命周期是无穷 的——从 lambda 表达式的实行 开始,只要存在对该值的任何引用。这意味着 lambda 从封闭方法中使用 或“捕获”的任何局部变量都必须在堆上分配。由于 lambda 值的生命周期不受产生它的堆栈帧的生命周期的限定 ,因此不能在该堆栈帧上分配变量。
    1. lambda
    复制代码
    表达式要求在实行 lambda 表达式时明确 分配主体中使用 的全部 外部变量。lambda 的第一次和末了 一次使用 的时间 很少是确定性的,因此该语言假设 lambda 值可以在创建后立刻 使用 ,只要它们是可访问的。因此,一个 lambda 值在创建时必须是完全可用的,并且它使用 的全部 外部变量都必须明确 分配。
  1. int x;
  2. // ERROR: 'x' is not definitely assigned
  3. Func<int> f = () => x;
复制代码
  • lambdas 没著名 字,也不能被象征性地引用。特别 是 lambda 表达式不能递归声明。

留意 :可以通过调用分配给 lambda 的变量或传递给自应用其参数的高阶方法来创建递归 lambda,但这不会表达真正的自我参照。

3、本地函数

局部函数基本上只是在另一个方法中声明的方法,作为一种降低方法对其声明范围内的可见性的方法。

天然 地,局部函数中的代码可以访问其包含范围内可访问的全部 内容——局部变量、封闭方法的参数、范例 参数、局部函数。一个值得留意 的破例 是外部方法标签的可见性。封闭方法的标签在局部函数中不可见。这只是寻常 的词法范围,它的工作原理与 lambdas 雷同 。

  1. public class C
  2. {
  3. object o;
  4. public void M1(int p)
  5. {
  6. int l = 123;
  7. // lambda has access to o, p, l,
  8. Action a = ()=> o = (p + l);
  9. }
  10. public void M2(int p)
  11. {
  12. int l = 123;
  13. // Local Function has access to o, p, l,
  14. void a()
  15. {
  16. o = (p + l);
  17. }
  18. }
  19. }
复制代码

与 lambda 的显着 区别在于局部函数具著名 称并且可以在没有任何间接方式的环境 下使用 。局部函数可以是递归的。

  1. static int Fac(int arg)
  2. {
  3. int FacRecursive(int a)
  4. {
  5. return a <= 1 ?
  6. 1 :
  7. a * FacRecursive(a - 1);
  8. }
  9. return FacRecursive(arg);
  10. }
复制代码

与 lambda 表达式的紧张 语义区别在于局部函数不是表达式,它们是声明语句。在代码实行 方面,声明黑白 常被动的实体。毕竟 上,声明并没有真正被“实行 ”。与标签等其他声明雷同 ,局部函数声明只是将函数引入包含范围,而无需运行任何代码。

更紧张 的是,无论是声明本身还是嵌套函数的常规调用都不会导致对环境的不确定捕获。在简单和常见的环境 下,如寻常 的调用/返回场景,捕获的局部变量不必要 举行 堆分配。

例子:

  1. public class C
  2. {
  3. public void M()
  4. {
  5. int num = 123;
  6. // has access to num
  7. void Nested()
  8. {
  9. num++;
  10. }
  11. Nested();
  12. System.Console.WriteLine(num);
  13. }
  14. }
复制代码

上面的代码大致相当 于(反编译):

  1. public class C
  2. {
  3. // A struct to hold "num" variable.
  4. // We are not storing it on the heap,
  5. // so it does not need to be a class
  6. private struct <>c__DisplayClass0_0
  7. {
  8. public int num;
  9. }
  10. public void M()
  11. {
  12. // reserve storage for "num" in a display struct on the _stack_
  13. C.<>c__DisplayClass0_0 env = default(C.<>c__DisplayClass0_0);
  14. // num = 123
  15. env.num = 123;
  16. // Nested()
  17. // note - passes env as an extra parameter
  18. C.<M>g__a0_0(ref env);
  19. // System.Console.WriteLine(num)
  20. Console.WriteLine(env.num);
  21. }
  22. // implementation of the the "Nested()".
  23. // note - takes env as an extra parameter
  24. // env is passed by reference so it's instance is shared
  25. // with the caller "M()"
  26. internal static void <M>g__a0_0(ref C.<>c__DisplayClass0_0 env)
  27. {
  28. env.num += 1;
  29. }
  30. }
复制代码

请留意 ,上面的代码直接调用了“Nested()”的实现(不是通过委托间接),并且没有在堆上引入表现 存储的分配(就像 lambda 会那样)。局部变量存储在布局 中而不是类中。的生命周期

  1. num
复制代码
并没有由于 它在 中的使用 而改变
  1. Nested
复制代码
(),以是 它仍旧 可以在栈上分配。
  1. M()
复制代码
可以只通过
  1. num
复制代码
引用传递,但编译器使用 布局 体举行 打包,因此它可以传递全部 本地变量,就像num只使用 一个
  1. env
复制代码
参数一样。

另一个风趣 的一点是,只要本地函数在给定范围内可见,就可以使用 它们。这是一个紧张 的毕竟 ,使递归和相互递归的场景成为大概 。这也使得本地函数声明在源代码中的确切位置在很大程度上变得不紧张 。

比方 ,封闭方法的全部 变量必须在调用读取它们的本地函数时明确 分配,而不是在其声明时。现实 上,假如 调用可以更早发生,那么在声明时提出该要求将没有任何好处。

  1. public void M()
  2. {
  3. // error here -
  4. // Use of unassigned local variable 'num'
  5. Nested();
  6. int num;
  7. // whether 'num' is assigned here or not is irrelevant
  8. void Nested()
  9. {
  10. num++;
  11. }
  12. num = 123;
  13. // no error here - 'num' is assigned
  14. Nested();
  15. System.Console.WriteLine(num);
  16. }
复制代码

此外 - 假如 从未使用 过局部函数,它也不会比一段无法访问的代码和任何变量更好,否则它会使用 ,不必要 分配。

  1. public void M()
  2. {
  3. int num;
  4. // warning - Nested() is never used.
  5. void Nested()
  6. {
  7. // no errors on unassigned 'num'.
  8. // this code never runs.
  9. num++;
  10. }
  11. }
复制代码

4、那么,局部函数的目的 是什么?

与 lambdas 相比,局部函数的紧张 价值主张是局部函数在概念上和运行时开销方面都更简单。

Lambda 可以很好地充当一类函数的脚色 ,但偶尔 您只必要 一个简单的助手。分配给局部变量的 Lambda 可以完成这项工作,但存在间接开销、委托分配和大概 的闭包开销。私有方法也有效 ,调用成本更低,但存在封装标题 ,或缺乏封装。如许 的助手对包含范例 中的每个人都是可见的。太多如许 的帮手会导致严肃 的混乱。

局部函数非常得当 这种环境 。调用本地函数的开销与调用私有方法的开销相当 ,但使用 其他不应调用的方法污染包含范例 没有标题 。

到此这篇关于C# 本地函数与 Lambda 表达式具体 先容 的文章就先容 到这了,更多干系 C# 本地函数与 Lambda 表达式内容请搜索 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

avatar 光荣与梦想483 | 2021-9-21 08:55:32 | 显示全部楼层
什么狗屁帖子啊,admin楼主的语文是苍老师教的吗?
回复

使用道具 举报

avatar 润唇膏贡 | 2021-9-22 08:47:04 | 显示全部楼层
读了admin楼主的帖子,顿时马桶就通了。。。
回复

使用道具 举报

admin楼主是一个神奇的青年!
回复

使用道具 举报

大神就是大神,这么经典!
回复

使用道具 举报

avatar 123457886 | 前天 03:16 | 显示全部楼层
支持一下!
回复

使用道具 举报

看帖不回帖的人就是耍流氓,我回复了!
回复

使用道具 举报

admin楼主写的很经典!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则