广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C# 本地函数与 Lambda 表达式详细介绍
  • 331
分享到

C# 本地函数与 Lambda 表达式详细介绍

2024-04-02 19:04:59 331人浏览 薄情痞子
摘要

目录1、C# 本地函数与 Lambda 表达式2、Lambda 表达式3、本地函数4、那么,局部函数的目的是什么?1、C# 本地函数与 Lambda 表达式 C# 局部函数通常被视为

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

C# 局部函数通常被视为 lambda 表达式的进一步增强。虽然功能是相关的,但也存在重大差异。

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

Lambda 或一般的一流函数需要实现未在堆栈上分配且生命周期与需要它们的功能对象相关联的局部变量。如果不依赖垃圾收集或通过捕获列表等解决方案将变量所有权的负担减轻给用户,则几乎不可能正确有效地实现它们。对于某些早期语言来说,这是一个严重的阻塞问题。嵌套函数的简单实现不会遇到这种复杂情况,因此一种语言更常见的是仅支持嵌套函数而不支持 lambda。

无论如何,由于 C# 长期以来一直使用 lambda,因此从差异和相似之处来看本地函数确实是有意义的。

2、Lambda 表达式

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


using System;
using System.Linq.Expressions;

class Program
{
    static void Main(string[] args)
    {
        // can't do much with the lambda expression directly
        // (x => x + x).ToString();  // error

        // can assign to a variable of delegate type and invoke
        Func<int, int> f = (x => x + x);
        System.Console.WriteLine(f(21)); // prints "42"

        // can assign to a variable of expression type and introspect
        Expression<Func<int, int>> e = (x => x + x);
        System.Console.WriteLine(e);     // prints "x => (x + x)"
    }
}

有几点值得注意:

  • lambdas 是产生函数值的表达式。
  • lambda 值的生命周期是无限的——从 lambda 表达式的执行开始,只要存在对该值的任何引用。这意味着 lambda 从封闭方法中使用或“捕获”的任何局部变量都必须在堆上分配。由于 lambda 值的生命周期不受产生它的堆栈帧的生命周期的限制,因此不能在该堆栈帧上分配变量。
  • lambda 表达式要求在执行 lambda 表达式时明确分配主体中使用的所有外部变量。lambda 的第一次和最后一次使用的时刻很少是确定性的,因此该语言假设 lambda 值可以在创建后立即使用,只要它们是可访问的。因此,一个 lambda 值在创建时必须是完全可用的,并且它使用的所有外部变量都必须明确分配。

        int x;

        // ERROR: 'x' is not definitely assigned
        Func<int> f = () => x;
  • lambdas 没有名字,也不能被象征性地引用。特别是 lambda 表达式不能递归声明。

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

3、本地函数

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

自然地,局部函数中的代码可以访问其包含范围内可访问的所有内容——局部变量、封闭方法的参数、类型参数、局部函数。一个值得注意的例外是外部方法标签的可见性。封闭方法的标签在局部函数中不可见。这只是普通的词法范围,它的工作原理与 lambdas 相同。


public class C
{
    object o;

    public void M1(int p)
    {
        int l = 123;

        // lambda has access to o, p, l,
        Action a = ()=> o = (p + l);
    }

    public void M2(int p)
    {
        int l = 123;

        // Local Function has access to o, p, l,
        void a()
        {
          o = (p + l);
        }
    }
}

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


static int Fac(int arg)
{
    int FacRecursive(int a)
    {
        return a <= 1 ?
                    1 :
                    a * FacRecursive(a - 1);
    }

    return FacRecursive(arg);
}

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

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

例子:


public class C
{    
    public void M()
    {
        int num = 123;

        // has access to num
        void  Nested()
        {
           num++;
        }

        Nested();

        System.Console.WriteLine(num);
    }
}

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


public class C
{
  // A struct to hold "num" variable.
  // We are not storing it on the heap,
  // so it does not need to be a class
  private struct <>c__DisplayClass0_0
  {
      public int num;
  }

  public void M()
  {
      // reserve storage for "num" in a display struct on the _stack_
      C.<>c__DisplayClass0_0 env = default(C.<>c__DisplayClass0_0);

      // num = 123
      env.num = 123;

      // Nested()
      // note - passes env as an extra parameter
      C.<M>g__a0_0(ref env);

      // System.Console.WriteLine(num)
      Console.WriteLine(env.num);
  }

    // implementation of the the "Nested()".
    // note - takes env as an extra parameter
    // env is passed by reference so it's instance is shared
    // with the caller "M()"
    internal static void <M>g__a0_0(ref C.<>c__DisplayClass0_0 env)
    {
        env.num += 1;
    }
}

请注意,上面的代码直接调用了“Nested()”的实现(不是通过委托间接),并且没有在堆上引入显示存储的分配(就像 lambda 会那样)。局部变量存储在结构中而不是类中。的生命周期num并没有因为它在 中的使用而改变Nested(),所以它仍然可以在栈上分配。M()可以只通过num引用传递,但编译器使用结构体进行打包,因此它可以传递所有本地变量,就像num只使用一个 env 参数一样。

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

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


public void M()
{
    // error here -
    // Use of unassigned local variable 'num'
    Nested();

    int num;

    // whether 'num' is assigned here or not is irrelevant
    void  Nested()
    {
       num++;
    }

    num = 123;

    // no error here - 'num' is assigned
    Nested();

    System.Console.WriteLine(num);
}

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


public void M()
{        
    int num;

    // warning - Nested() is never used.
    void  Nested()
    {
       // no errors on unassigned 'num'.
       // this code never runs.
       num++;
    }
}

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

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

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

局部函数非常适合这种情况。调用本地函数的开销与调用私有方法的开销相当,但使用其他不应调用的方法污染包含类型没有问题。

到此这篇关于C# 本地函数与 Lambda 表达式详细介绍的文章就介绍到这了,更多相关C# 本地函数与 Lambda 表达式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C# 本地函数与 Lambda 表达式详细介绍

本文链接: https://www.lsjlt.com/news/135022.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C# 本地函数与 Lambda 表达式详细介绍
    目录1、C# 本地函数与 Lambda 表达式2、Lambda 表达式3、本地函数4、那么,局部函数的目的是什么?1、C# 本地函数与 Lambda 表达式 C# 局部函数通常被视为...
    99+
    2022-11-12
  • C++11 lambda(匿名函数)表达式详细介绍
    目录前言概念及基本用法捕获变量lambda表达式类型声明式的编程风格总结前言 Lambda(匿名函数)表达式是C++11最重要的特性之一,lambda来源于函数式编程的概念,也是现代...
    99+
    2022-11-13
  • 关于java中的Lambda表达式的详细介绍
    什么是lambda表达式?lambda表达式是一个可传递的代码块,可以在后面执行一次或多次。推荐java相关视频教程:java学习视频例如:class action implements ActionListener{ @Override...
    99+
    2016-04-10
    java入门 java lambda表达式
  • C++详细分析lambda表达式的本质
    例: stable_sort(words.begin(), words.end(), [](const string& s1, const string& s2...
    99+
    2022-11-13
  • Python字符串与正则表达式详细介绍
    目录一、字符串相关操作 二、正则表达式相关操作一、字符串相关操作  1.统计所输入字符串中单词的个数,单词之间用空格分隔。其运行效果如下图所示。 s=input(...
    99+
    2022-11-13
  • 详解Kotlin 高阶函数 与 Lambda 表达式
    详解Kotlin 高阶函数 与 Lambda 表达式高阶函数(higher-order function)是一种特殊的函数, 它接受函数作为参数, 或者返回一个函数. 这种函数的一个很好的例子就是 lock() 函数, 它的参数是一个锁对象...
    99+
    2023-05-31
    kotlin 高阶函数 lambda
  • Kotlin高阶函数与Lambda表达式示例详解
    目录正文1.函数类型2.高阶函数3.系统标准高阶函数4.Lambda表达式正文 Kotlin中函数都是头等的,这意味着它可以存储在变量与数据结构中、作为参数传递给其他高阶函数以及从...
    99+
    2022-12-08
    Kotlin 高阶函数Lambda Kotlin Lambda
  • C++多态特性之派生与虚函数与模板详细介绍
    目录继承与派生虚函数父类代码如下模板函数模板类模板字符串继承与派生 C ++ 是面向对象编程,那么只要面向对象,都会有多态、继承的特性。C++是如何实现继承的呢? 继承(Inheri...
    99+
    2022-11-13
  • lambda表达式与传统接口函数实现方式对比详解
    目录一、接口定义二、传统的接口函数实现方式三、lambda表示式实现方式结论 在本号之前写过的一些文章中,笔者使用了lambda表达式语法,一些读者反映说代码看不懂。本以为java ...
    99+
    2022-11-13
  • 阿里云数据库备份到本地文件的详细介绍与应用
    随着互联网的发展,数据的重要性日益凸显,因此数据库的备份成为了不可或缺的一项工作。阿里云作为国内领先的云服务提供商,提供了一系列的数据库备份方案,其中一种就是将数据库备份到本地文件。本文将详细介绍阿里云如何实现数据库备份到本地文件,并探讨其...
    99+
    2023-11-04
    阿里 详细介绍 备份
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作