【C#杂谈】在 .NET Framework 中使用新的C#语言特性

前排提示:提出一个可以让 [^1] 这中语法可以在.NET Framework运行时中使用的方法

众所都周知,.NET Framework(以下简称 .NF)作为一个被微软官方确认不在继续发布新特性的运行时,它所对应的C#语言版本被(官方形式上)永久地停更在了 C# 7.3(对应着 .NF 4.8,如果是更早版本的 .NF,那么其语言版本可能更古早)。

但是,由于C#是语言,而.NF是实现该语言的运行时,如果某些语言特性能够在 .NF 的框架下实现,那么我们实际上还是能在 Visual Studio 等IDE上直接通过修改对应的 .csproj 文件,增加 <LangVersion>,来使用新的语言特性的。

运行时与语言的关系就类似于……我用口头说话来指挥雇佣工干活,我说的话(语言)和他能干的活(运行时)一般来说是没有一一对应的关系的。

C#更高级的语言可以认为是我会更多的词汇,但是如果这个词汇所代表的特性能够被现有的运行时来实现,那么新版本的C#语言也是可以使用 .NF 来编译运行。

比如我比较喜欢的 Pattern Matching enhancements (C# 9.0),就能让我们在代码里使用 is not 的关键词。

if (e is not string { Length: > 5} short_str)
{// ... 如果e不满足“非空”且“长度大于5”的条件
}

这本质上是一些语法糖,所以 .NF 是支持这些语法的。

微软官方对于这些通过改 .csproj 文件就能实现的功能具体有哪些并没有写明的文档可以参考,有的只是运行时对应的“保证语言特性100%支持的版本”,它列在了下面的网页中。

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version

C# 12 is supported only on .NET 8 and newer versions. C# 11 is supported only on .NET 7 and newer versions. C# 10 is supported only on .NET 6 and newer versions.

我们知道当然不是如此,某些C# 12的功能也能在 .NF 下使用,比如 Primary constructors(前提是更新 Visual Studio 2022 到版本 17.6 以上,总之更新到最新就对了)。

C#语言的新功能在新的运行时能力加持下,可能会带来执行速度的提升,但是在旧的运行时版本下,可能只是语法糖。

不过!就算是语法糖,那也能极大地提高代码可读性,减少出错,比如上面的 e is not string,那就比 !(e is string) 更容易理解,因为后者再叠加上if,就变成了

if (!(e is string))

布尔取反被夹在了俩括号之间,不小心就看错了,尤其是对新手而言。我认为一个好的高级语言一定是能够帮助程序员更加专注于业务逻辑而非语法本身的。所以,这种新特性虽然不能提高执行效率,但我仍然推荐大家使用。

下面我就稍微列一下我个人最喜欢的语言特性【排名部分先后】:

  1. 模式匹配 https://learn.microsoft.com/zh-cn/dotnet/csharp/fundamentals/functional/pattern-matching
  2. 主构造函数 https://learn.microsoft.com/zh-cn/dotnet/csharp/whats-new/tutorials/primary-constructors
  3. 全局using指令 https://learn.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-10#global-using-directives
  4. 原始字符串文本 https://learn.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-11#raw-string-literals
  5. 从末尾运算符 ^ 开始索引 https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/member-access-operators#index-from-end-operator-

俗话说,重要的事情留到最后,没错,今天的主角就是 5. 从末尾运算符 ^ 开始索引

^ 是个啥

相信很多读者必写过这样的代码:

var last = list[list.Count - 1];

噢!那丑陋的.Count - 1 。如果变量名再长点,再多点额外的业务逻辑:

var diff = some_sort_of_list[some_sort_of_list.Count - 2] - some_sort_of_list[some_sort_of_list.Count - 1];

一般在写这种东西的时候我就已经开始换行了。这个时候有些人就想起了 System.Linq 命名空间里的 Last() 方法。它确实可以拯救 .Count - 1,但是这救不了 .Count - 2啊!。

C# 8 中就有一个解决这个问题的方法,用 [^1] 来代表向前数倒数第一个,[^n] 就代表倒数第n个。这也太直观了。

var diff = some_sort_of_list[^2] - some_sort_of_list[^1];

方便阅读,不会出错。

但是!!! 事情并没有那么简单。如果直接新建一个 .NET Framework 的 命令行项目,修改 <LangVersion>latest(使用最新)

在这里插入图片描述

Main 函数里写下下面的代码,我们会发现无法编译,IDE会报一个很奇怪的错误:

在这里插入图片描述

System.Index类找不到”、“缺失编译器需要的成员…ctor”,这都什么错误??

解决 [^1] 无法编译的问题

这个问题其实就是 .NF 框架的问题。C# 8.0的这个 “从后往前数” 的新的语言特性需要运行时中包含有一个System.Index类,这样它在编译的时候就直接用这个类去支持该特性了。但是由于 .NF 的运行时默认不包含该类,那就自然无法直接使用该语言特性了。

简而言之,就是[^1]这种语法需要运行时包含System.Index类,但是.NF中内置没有包含,所以GG。

不过,既然本节的标题是“解决”,那么事情必然是有转机的。在笔者翻了外网各种奇奇怪怪的论坛之后,得出的结论是“如果.NF没有这个类,那么我们自己提供一个就可以了!!! ”。

妙啊!

直接新建一个类,起名 Index.cs,粘贴入大佬的代码(见本文最后)………… 编译通过,运行成功!

在这里插入图片描述

这回,代码可读性又更强了。【聪明的读者已经去尝试 范围运算符 了】

// Modified after: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Index.cs
// MIT licensed.
#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace System;
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly struct Index : IEquatable<Index>
{private readonly int m_value;[MethodImpl(MethodImplOptions.AggressiveInlining)]public Index(int value, bool fromEnd = false){if (value < 0){throw new ArgumentOutOfRangeException(nameof(value));}if (fromEnd){m_value = ~value;}else{m_value = value;}}private Index(int value){m_value = value;}public static Index Start => new Index(0);public static Index End => new Index(~0);public int Value => m_value < 0 ? ~m_value : m_value;public bool IsFromEnd => m_value < 0;public static implicit operator Index(int value) => FromStart(value);public static bool operator ==(Index left, Index right) => left.Equals(right);public static bool operator !=(Index left, Index right) => !(left == right);[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Index FromStart(int value){if (value < 0){throw new ArgumentOutOfRangeException(nameof(value));}return new Index(value);}[MethodImpl(MethodImplOptions.AggressiveInlining)]public static Index FromEnd(int value){if (value < 0){throw new ArgumentOutOfRangeException(nameof(value));}return new Index(~value);}[MethodImpl(MethodImplOptions.AggressiveInlining)]public int GetOffset(int length) => IsFromEnd ? m_value + length + 1 : m_value;public override bool Equals(object value) => value is Index && m_value == ((Index)value).m_value;public bool Equals(Index other) => m_value == other.m_value;public override int GetHashCode() => m_value;public override string ToString() => IsFromEnd ? ToStringFromEnd() : ((uint)Value).ToString();private string ToStringFromEnd() => '^' + Value.ToString();
}
#endif

大佬代码出处(上面贴出来的代码删掉了注释并改了原来的一个小问题):

https://github.com/CptWesley/BackwardsCompatibleFeatures/blob/master/src/BackwardsCompatibleFeatures/Index.cs

关于范围运算符和其他类似的特性,原理也是一样的。找到对应的System.Range类,再额外提供几个方法就可以实现了。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/511960.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nuxt3:探索useFetch是如何做到避免客户端重复请求

在之前的文章《Nuxt3&#xff1a;useFetch在服务端及客户端重复请求问题》中提到了useFetch在服务端及客户端重复请求问题&#xff0c;文中提到Nuxt在服务端获取数据后&#xff0c;会将数据附加到Nuxt payload&#xff08;也就是nuxtApp.payload&#xff09;&#xff0c;它和文…

VMware虚拟机安装linux教程

CentOS7下载 下载 (centos.org)https://www.centos.org/download/新建虚拟机 选择自定义安装 这里要注意兼容性&#xff0c;如果是VMware12创建的虚拟机复制到VM11、10或者更低的版本会出现一不兼容的现象。如果是用VMware10创建的虚拟机在VMware12中打开则不会出现兼容性问题…

项目总体测试计划word

1. 引言 1.1 目的 1.2 适用范围 2. 测试类型 2.1 集成测试 2.2 系统测试 2.3 验收测试 2.4 回归测试 3. 测试环境 4. 测试工具 5. 测试内容 5.1 集成测试 5.1.1 集成测试角色与职责 5.1.2 集成测试产生的工作产品清单 5.2 系统测试 5.2.1 系统测试的角色与职责 5.2.2 系统测试产…

【C++】结构体类

文章目录 问题提出一、结构体1.1结构体的声明1.1.1正常定义的结构体1.1.2在声明结构体的同时声明变量1.1.3typedef1.1.4成员变量 1.2结构体成员变量的使用1.2.1成员运算符 .1.2.2成员运算符 -> 1.3内存对齐1.3.1什么是内存对齐1.3.2内存对齐原则1.3.3结构体成员的定义顺序 1…

打造禹州中医药大模型,以AI驱动业务创新(内附孙思邈GPT内测版)

大禹智库 第78 期&#xff08;总第409 期&#xff09; 2024年 3 月 4 日 在中医药传承与发展的关键时期&#xff0c;结合许昌市的地域特色和产业优势&#xff0c;大禹智库提出“打造禹州中医药大模型&#xff0c;以AI驱动业务创新”的战略构想。本报告围绕构建禹州中医药现代化…

Dockerfile(6) - EXPOSE 指令详解

EXPOSE 通知 Docker 容器在运行时监听指定的网络端口 EXPOSE 端口号 EXPOSE 端口号/协议 默认协议是 TCP 同时在 TCP、UDP 上暴露端口 EXPOSE 80/tcp EXPOSE 80/udp EXPOSE 原理 个人理解&#xff1a;EXPOSE 暴露的端口更像是指明了该容器提供的服务需要用到的端口EXPOSE…

『运维备忘录』之 RegEx 正则表达式实例汇总

运维人员不仅要熟悉操作系统、服务器、网络等知识&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

这本书太好了!150页就能让你上手大模型应用开发

如果问个问题&#xff1a;有哪些产品曾经创造了伟大的奇迹&#xff1f;ChatGPT 应该会当之无愧入选。仅仅发布 5 天&#xff0c;ChatGPT 就吸引了 100 万用户——当然&#xff0c;数据不是关键&#xff0c;关键是其背后的技术开启了新的 AI 狂潮&#xff0c;成为技术变革的点火…

数据治理实战——翼支付金融板块业务数仓建设和数据治理之路

目录 一、数据治理背景 二、数据治理建设内容 2.1 组织协同 2.2 平台建设 2.3 数据应用治理 2.4 数据规范 2.5 数据安全 三、企业级数仓建设 3.1 调研阶段 2.2 平台护航 2.3 数仓分层 2.4 维度建模 2.4.1 维度建模四步曲 2.4.2 命名规范 2.4.3 资产沉淀 2.4.4 …

基于springboot+vue的新闻资讯系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

基于springboot+vue的美食烹饪互动平台

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

2024年山东省安全员C证证模拟考试题库及山东省安全员C证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年山东省安全员C证证模拟考试题库及山东省安全员C证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;山东省安全员C证证模拟考试题库是根据山东省安全员C证最新版教材&#xff0c;山东省安全员C证大纲整理…