首页 程序笔记 .NET8 C#12 使用主构造函数重构代码

.NET8 C#12 使用主构造函数重构代码

C# 12 作为 .NET 8 的一部分引入了一组引人注目的新功能!在这篇文章中,我们将探讨其中一个功能,特别是主要构造函数,解释其用法和相关性。然后,我们将演示一个示例重构,以展示如何将其应用到你的代码中,并讨论其好处和潜在的缺陷。这将帮助你了解更改的影响并帮助影响你对该功能的采用。

主构造函数

主构造函数被认为是“日常 C#”开发人员功能。它们允许你在一个简洁的声明中定义类或结构及其构造函数。这可以帮助你减少需要编写的样板代码量。如果你一直在关注 C# 版本,你可能熟悉记录类型,其中包括主构造函数的第一个示例。

与记录类型的区别

记录类型作为类或结构的类型修饰符引入,它简化了构建简单类(如数据容器)的语法。记录可以包括主构造函数。该构造函数不仅生成一个支持字段,而且还为每个参数公开一个公共属性。与传统的类或结构类型不同,在传统的类或结构类型中,主构造函数参数可以在整个类定义中访问,而记录被设计为透明的数据容器。他们本质上支持基于价值的平等,这与他们作为数据持有者的预期角色相一致。因此,它们的主要构造函数参数可以作为属性访问是合乎逻辑的。

重构示例

.NET 提供了许多模板,如果你曾经创建过 Worker Service,你可能已经看到过以下 Worker 类模板代码:

namespace Example.Worker.Service
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                if (_logger.IsEnabled(LogLevel.Information))
                {
                    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                }
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

前面的代码是一个简单的 Worker 服务,每秒记录一条消息。目前,Worker 类有一个构造函数,需要一个 ILogger<Worker> 实例作为参数,并将其分配给相同类型的只读字段。此类型信息位于两个位置,即构造函数的定义中,以及字段本身。这是 C# 代码中的常见模式,但可以使用主构造函数进行简化。

值得一提的是,Visual Studio Code 中不提供此特定功能的重构工具,但你仍然可以手动重构主构造函数。要在 Visual Studio 中使用主构造函数重构此代码,可以使用“使用主构造函数(并删除字段)”重构选项。右键单击 Worker 构造函数,选择“快速操作和重构...”(或按 Ctrl + .),然后选择“使用主构造函数”(并删除字段)。

现在生成的代码类似于以下 C# 代码:

namespace Example.Worker.Service
{
    public class Worker(ILogger<Worker> logger) : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                if (logger.IsEnabled(LogLevel.Information))
                {
                    logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                }
                await Task.Delay(1000, stoppingToken);
            }
        }
    }
}

就这样,你已经成功重构了 Worker 类以使用主构造函数! ILogger<Worker> 字段已被删除,并且构造函数已替换为主构造函数。这使得代码更加简洁,更容易阅读。记录器实例现在在整个类中可用(因为它在范围内),而不需要单独的字段声明。

其他注意事项

主构造函数可以删除在构造函数中分配的手写字段声明,但有一个警告。如果你将字段定义为只读,那么它们在功能上并不完全等效,因为非记录类型的主构造函数参数是可变的。因此,当你使用这种重构方法时,请注意你正在更改代码的语义。如果要保持只读行为,请就地使用字段声明并使用主构造函数参数分配该字段:

namespace Example.Worker.Service;

public class Worker(ILogger<Worker> logger) : BackgroundService
{
    private readonly ILogger<Worker> _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (_logger.IsEnabled(LogLevel.Information))
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            }
            await Task.Delay(1000, stoppingToken);
        }
    }
}

额外的构造函数

当你定义主构造函数时,你仍然可以定义其他构造函数。然而,这些构造函数是必需的;调用主构造函数。调用主构造函数可确保主构造函数参数在类声明中的所有位置进行初始化。如果需要定义其他构造函数,则必须使用 this 关键字调用主构造函数。

namespace Example.Worker.Service
{
    // Primary constructor
    public class Worker(ILogger<Worker> logger) : BackgroundService
    {
        private readonly int _delayDuration = 1_000;

        // Secondary constructor, calling the primary constructor
        public Worker(ILogger<Worker> logger, int delayDuration) : this(logger)
        {
            _delayDuration = delayDuration;
        }

        // Omitted for brevity...
    }
}

并不总是需要额外的构造函数。让我们进行一些额外的重构以包含一些其他功能!

奖励重构

主构造函数非常棒,但是我们还可以做更多的事情来改进代码。

C# 包含文件范围的命名空间。它们是一个非常好的功能,可以减少嵌套级别并提高可读性。继续前面的示例,将光标放在命名空间名称的末尾,然后按 ;键(Visual Studio Code 不支持此操作,但你也可以手动执行此操作)。这会将命名空间转换为文件范围的命名空间。

经过一些额外的编辑,最终的重构代码如下:

namespace Example.Worker.Service;

public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (logger.IsEnabled(LogLevel.Information))
            {
                logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            }

            await Task.Delay(1_000, stoppingToken);
        }
    }
}

除了重构文件范围的命名空间之外,我还添加了 seal 修饰符,因为在多种情况下都有性能优势。最后,我还使用数字分隔符功能更新了传递到 Task.Delay 的数字文字,以提高可读性。你知道还有很多方法可以简化你的代码吗?查看 C# 中的新增功能以了解更多信息!

1

站心网

C# 12 作为 .NET 8 的一部分引入了一组引人注目的新功能!在这篇文章中,我们将探讨其中一个功能,特别是主..

为您推荐

.NET C# 过滤从富文本编辑器html里的Javascript脚本

富文本编辑器在允许用户输入丰富内容的同时,也带来了跨站脚本攻击(XSS)的风险。过滤提交的 HTML 中的 <script> 脚本是防止跨站脚本攻击(XSS)的关键步骤。在 .NET C# 服务端过滤 <script> 脚本主要有以下几种方..

ZLinq:.NET 高性能 LINQ 替代方案及其使用指南

在 .NET 开发中,LINQ(Language Integrated Query)为数据查询提供了简洁且强大的语法。然而,传统的 LINQ 在处理大量数据时可能会引发性能瓶颈,主要由于频繁的内存分配和对象创建。为解决这一问题,Cysharp 团队..

.NET使用AutoMapper简化对象映射

在.NET软件开发中,常常需要将一个对象的数据转换并映射到另一个对象上。​这种手动映射的过程既繁琐又容易出错,影响开发效率和代码可维护性。​为了解决这一问题,AutoMapper应运而生。​什么是 AutoMapper?AutoM..

.NET C# RESTful API交互Refit库使用教程

Refit 是一个 .NET C# 库,它简化了与 RESTful API 的交互。Refit 受到 Square 的 Retrofit 库的启发,它将 REST API 转换为实时接口,允许你以声明方式定义 REST API 调用。Refit 的特点1. 声明式 API 定义:Refit ..

.NET C# System.Text.Json进阶使用技巧

System.Text.Json 是 .NET 中用于处理 JSON 数据的强大库。除了基本用法外,它还提供了许多进阶技巧,可以帮助你更高效、更灵活地处理 JSON 数据。以下是一些 System.Text.Json 的进阶使用技巧:1. 自定义序列化和反..

.NET Core 使用ML.NET 机器学习分析预测股票走势

在 .NET Core 中,你可以利用 ML.NET 框架来构建机器学习模型,以预测股票价格走势。以下是一个基本的实现步骤:​1. 准备数据:​收集并整理股票的历史数据,包括日期、开盘价、最高价、最低价、收盘价和成交量等信..

代码照进现实:对公司管理策略的技术性解构

上学的时候觉得计算机专业的一些理论晦涩难懂,跟现实世界的关联太少,每当遇到一些精妙的设计时都会发出一种感叹:究竟是什么脑袋才能想出这么有意思的东西。一晃工作十年,阅历渐丰,隐约发现其实社会中的一些现象..

.NET 日志库 Serilog 使用教程

1. Serilog 简介Serilog 是 .NET 生态中强大且灵活的日志库,支持结构化日志记录,并提供多种日志接收器(Sinks),可以将日志输出到控制台、文件、数据库等不同存储介质。Serilog 适用于控制台应用、ASP.NET Core ..

编写优秀 CSS 代码的 8 个策略

编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一。然而,我遇到的很多应用程序显然没有人花时间真正考虑前端开发的长久性和可维护性。我认为这主要是因为许多开发人员对组织CSS / HTML和JavaScript的策..

天天写业务代码,如何成为技术大牛?

不管是开发、测试、运维,每个技术人员心理多多少少都有一个成为技术大牛的梦,毕竟"梦想总是要有的,万一实现了呢"!正是对技术梦的追求,促使我们不断地努力和提升自己。然而"梦想是美好的,现实却是残酷的",很多..

记我经历的一次公司破产经历,一行代码害死一家公司

前言这是一篇亲身经历的真实记录,事情发生在2010年。狗血剧情一再上演,使我的程序员生涯变得跌宕起伏,也从中学到了很多。在写这篇文章之前,我还专门去查了这家公司的资料。有如下事实:1.官网已经打不开了。2.天..

如何处理前任程序员留下的代码

作为软件工程师不可避免会遇到的一个场景是:我们在改变或添加一个功能到不是我们创建的、我们不熟悉的、与我们负责的系统部分无关的代码中时,会遇到麻烦。虽然这可能会是一个繁琐而艰巨的任务,但是由于使用其他开..

对码农而言什么样的代码才能叫做好代码?

好的代码,就像是好的笑话——无需解释就能让别人明白。如果你的代码能够做到不解自明,在大多数时候,你根本无需为其配备说明文档。好的代码,就像是一辆配备了优秀音响和杯架的汽车,这辆车在行驶到最高速度的时候..

当一个程序员写不出代码了,该怎么办?

你已经对着电脑n个小时了。不知道该写什么代码,或者一种摔键盘的冲动正在你的胸中酝酿。咖啡一杯接着一杯。不敢再喝了,因为搞不好要有副作用了,心跳加速,身体不由自主地颤抖,出冷汗,但还是无法产出任何代码。..

.NET C# 单元测试 mock File.Exists的返回值

在 .NET 单元测试中,使用 Moq 来模拟 File.Exists 方法的返回值,可以这样做:1. 使用 Mock<FileSystem>(推荐).NET 提供了 System.IO.Abstractions 库,你可以使用 Mock<IFileSystem> 来替代 File,这样更符合依..

.NET Core 适配 鸿蒙HarmonyOS 的最新进展

.NET Core适配鸿蒙HarmonyOS的最新进展:运行能力方面目前.Net完全具备可以在OpenHarmony系统上运行的能力。其中,NativeAOT方式是较为可行的一种,它编译出的原生so不依赖glibc,可与鸿蒙系统的libc兼容,能在鸿蒙..

VS创建.NET Core项目使用Docker方式部署到Linux服务器

在 Visual Studio(VS) 中,使用 Docker 方式部署 .NET Core 项目 到 Linux 服务器,可以简化环境管理并提高部署效率。以下是完整教程:1. 在 VS 创建 .NET Core 项目并启用 Docker新建 ASP.NET Core 项目打开 Visu..

.NET C#查询全球IP地址信息 IPTools库 使用教程

IPTools 是一个用于快速查询全球 IP 地址信息的库,支持国内和国际 IP 查询,提供详细的地理位置信息(如国家、省份、城市)以及经纬度等数据。IPTools GitHub地址:https://github.com/stulzq/IPToolsIPTools.China..

2025年.NET 10 和 C# 13 新特性示例

.NET 10预计将于2025年11月正式发布,带来一系列性能优化、开发者效率提升和跨平台能力增强。尽管官方功能集仍在开发中,早期预览版与社区讨论已揭示了多项值得期待的改进。这些增强将基于.NET 9的基础,引入新语言..

.NET Core网站减少内存占用的方法

在.NET Core网站开发中,有效管理内存占用对于保证应用程序的性能和稳定性至关重要。以下是一些减少内存占用的关键策略,它们着重于代码优化、内存管理以及相关因素的综合考虑。代码层面的优化首先,编写高效的代码..

发表回复

返回顶部