您的位置:首页 > 科技 > 能源 > 网页制作素材可爱图片_手工制作生日礼物_网络营销策划与创意_免费推广引流平台有哪些

网页制作素材可爱图片_手工制作生日礼物_网络营销策划与创意_免费推广引流平台有哪些

2024/12/23 7:31:48 来源:https://blog.csdn.net/m0_72813396/article/details/144479457  浏览:    关键词:网页制作素材可爱图片_手工制作生日礼物_网络营销策划与创意_免费推广引流平台有哪些
网页制作素材可爱图片_手工制作生日礼物_网络营销策划与创意_免费推广引流平台有哪些

乍看之下,本地函数和 lambda 表达式非常相似。 在许多情况下,选择使用 Lambda 表达式还是本地函数是风格和个人偏好的问题。 但是,应该注意,从两者中选用一种的时机和条件其实是存在差别的。

让我们检查一下阶乘算法的本地函数实现和 lambda 表达式实现之间的差异。 下面是使用本地函数的版本:

public static int LocalFunctionFactorial(int n)
{return nthFactorial(n);int nthFactorial(int number) => number < 2 ? 1 : number * nthFactorial(number - 1);
}

此版本使用 Lambda 表达式:

public static int LambdaFactorial(int n)
{Func<int, int> nthFactorial = default(Func<int, int>);nthFactorial = number => number < 2? 1: number * nthFactorial(number - 1);return nthFactorial(n);
}
命名

本地函数的命名方式与方法相同。 Lambda 表达式是一种匿名方法,需要分配给 delegate 类型的变量,通常是 Action 或 Func 类型。 声明本地函数时,此过程类似于编写普通方法;声明一个返回类型和一个函数签名。

函数签名和 Lambda 表达式类型

Lambda 表达式依赖于为其分配的 Action/Func 变量的类型来确定参数和返回类型。 在本地函数中,因为语法非常类似于编写常规方法,所以参数类型和返回类型已经是函数声明的一部分。

从 C# 10 开始,某些 Lambda 表达式具有自然类型,这使编译器能够推断 Lambda 表达式的返回类型和参数类型。

明确赋值

Lambda 表达式是在运行时声明和分配的对象。 若要使用 Lambda 表达式,需要对其进行明确赋值:必须声明要分配给它的 Action/Func 变量,并为其分配 Lambda 表达式。 请注意,LambdaFactorial 必须先声明和初始化 Lambda 表达式 nthFactorial,然后再对其进行定义。 否则,会导致分配前引用 nthFactorial 时出现编译时错误。

本地函数在编译时定义。
 由于未将它们分配给变量,因此可以从范围内的任意代码位置引用它们;在第一个示例 LocalFunctionFactorial 中,我们可以在 return 语句的上方或下方声明本地函数,而不会触发任何编译器错误。

这些区别意味着使用本地函数创建递归算法会更轻松。 你可以声明和定义一个调用自身的本地函数。 必须声明 Lambda 表达式,赋给默认值,然后才能将其重新赋给引用相同 Lambda 表达式的主体。

实现为委托

Lambda 表达式在声明时转换为委托。 本地函数更加灵活,可以像传统方法一样编写,也可以作为委托编写。 只有在用作委托时,本地函数才转换为委托。

如果声明了本地函数,但只是通过像调用方法一样调用该函数来引用该函数,它将不会转换成委托。

变量捕获

明确分配的规则也会影响本地函数或 Lambda 表达式捕获的任何变量。 编译器可以执行静态分析,因此本地函数能够在封闭范围内明确分配捕获的变量。 请看以下示例:

int M()
{int y;LocalFunction();return y;void LocalFunction() => y = 0;
}

编译器可以确定 LocalFunction 在调用时明确分配 y。 因为在 return 语句之前调用了 LocalFunction,所以在 return 语句中明确分配了 y。

请注意,当本地函数捕获封闭作用域中的变量时,该本地函数会使用闭包来实现,就像委托类型一样。

堆分配

根据它们的用途,本地函数可以避免 Lambda 表达式始终需要的堆分配。 如果本地函数永远不会转换为委托,并且本地函数捕获的变量都不会被其他转换为委托的 lambda 或本地函数捕获,则编译器可以避免堆分配。

请看以下异步示例:

public async Task<string> PerformLongRunningWorkLambda(string address, int index, string name)
{if (string.IsNullOrWhiteSpace(address))throw new ArgumentException(message: "An address is required", paramName: nameof(address));if (index < 0)throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");if (string.IsNullOrWhiteSpace(name))throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));Func<Task<string>> longRunningWorkImplementation = async () =>{var interimResult = await FirstWork(address);var secondResult = await SecondStep(index, name);return $"The results are {interimResult} and {secondResult}. Enjoy.";};return await longRunningWorkImplementation();
}

该 lambda 表达式的闭包包含 address、index 和 name 变量。 就本地函数而言,实现闭包的对象可能为 struct 类型。 该结构类型将通过引用传递给本地函数。 实现中的这个差异将保存在分配上。

Lambda 表达式所需的实例化意味着额外的内存分配,后者可能是时间关键代码路径中的性能因素。 本地函数不会产生这种开销。 在以上示例中,本地函数版本具有的分配比 Lambda 表达式版本少 2 个。

如果你知道本地函数不会转换为委托,并且本地函数捕获的变量都不会被其他转换为委托的 lambda 或本地函数捕获,则可以通过将本地函数声明为 static 本地函数来确保避免在堆上对其进行分配。

启用 .NET 代码样式规则 IDE0062 以确保始终标记本地函数static。

等效于此方法的本地函数还将类用于闭包。 实现详细信息包括本地函数的闭包是作为 class 还是 struct 实现。 本地函数可能使用 struct,而 lambda 将始终使用 class。

public async Task<string> PerformLongRunningWork(string address, int index, string name)
{if (string.IsNullOrWhiteSpace(address))throw new ArgumentException(message: "An address is required", paramName: nameof(address));if (index < 0)throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");if (string.IsNullOrWhiteSpace(name))throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));return await longRunningWorkImplementation();async Task<string> longRunningWorkImplementation(){var interimResult = await FirstWork(address);var secondResult = await SecondStep(index, name);return $"The results are {interimResult} and {secondResult}. Enjoy.";}
}
yield 关键字的用法

在本示例中尚未演示的最后一个优点是,可将本地函数作为迭代器实现,使用 yield return 语法生成一系列值。

public IEnumerable<string> SequenceToLowercase(IEnumerable<string> input)
{if (!input.Any()){throw new ArgumentException("There are no items to convert to lowercase.");}return LowercaseIterator();IEnumerable<string> LowercaseIterator(){foreach (var output in input.Select(item => item.ToLower())){yield return output;}}
}

Lambda 表达式中不允许使用 yield return 语句。 

虽然本地函数对 lambda 表达式可能有点冗余,但实际上它们的目的和用法都不一样。 如果想要编写仅从上下文或其他方法中调用的函数,则使用本地函数更高效。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com