【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类,再额外提供几个方法就可以实现了。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TruEra

文章目录 关于 TruEra关于 TruLens 关于 TruEra TruEra Gen AI Observability and LLM Evaluation​ Monitor, evaluate, and debug your LLM and Gen AI apps. All part of Full Lifecycle AI Observability from TruEra. 官网&#xff1a;https://truera.comgithub : https…

时间序列分析技巧(一):根据ACF、PACF进行AR、MA、ARMA模型选择

程序员如何选择职业赛道&#xff1f; &#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#…

定时执行专家 - 自动截屏任务的设置步骤

以下是使用定时执行专家进行自动截屏的设置步骤&#xff1a; 下载并安装定时执行专家 从以下 官方博客 下载最新版本的定时执行专家&#xff0c;并按照提示进行安装。 BoomWorks软件的最新版-CSDN博客文章浏览阅读10w次&#xff0c;点赞9次&#xff0c;收藏42次。▉定时执行…

C语言数据结构与算法——深度、广度优先搜索(DFS、BFS)

目录 一、深度优先搜索&#xff08;Depth-First-Search 简称&#xff1a;DFS&#xff09; 无向图的深度优先搜索 有向图的深度优先搜索 二、广度优先搜索&#xff08;Breadth-First-Search 简称&#xff1a;BFS&#xff09; 无向图的广度优先搜索 有向图的广度优先搜索 深…

市场复盘总结 20240305

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 25% 最常用的…

CSS字体样式值,html注释标签

突破困境&#xff1a; 1. 提升学历 前端找工作&#xff0c;学历重要吗&#xff1f; 重要。谁要是告诉你不重要那一定是在骗你。现实情况是大专吃紧&#xff0c;本科够用&#xff0c;硕士占优&#xff0c;大专以下找到工作靠运气和真实力。 学历是硬伤&#xff0c;已经毕业的你…

(黑马出品_02)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_02&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术栈治理 今日目标1.Nacos配置管理1.1.统一配置管理1.1.1.在nacos中添加配置文件1.1.2.从微服务拉取配置 1.2.配置热更新1.2.1.方式一1.2.2.方式二 1.3.配…

【Java设计模式】六、代理模式:静态代理、JDK + CGLIB动态代理

文章目录 1、代理对象2、代理模式结构3、静态代理4、JDK动态代理5、JDK动态代理的原理6、CGLIB动态代理7、三种代理的对比8、代理模式的总结 结构型设计是将类或者对象按某种布局&#xff08;继承机制、组合聚合&#xff09;来组成更大结构。包括七种&#xff1a; * 代理模式 …

代码随想录第51天|● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费 ●总结

文章目录 ● 309.最佳买卖股票时机含冷冻期思路代码 ● 714.买卖股票的最佳时机含手续费思路&#xff1a; ●总结 ● 309.最佳买卖股票时机含冷冻期 思路 代码 class Solution {public int maxProfit(int[] prices) {// 0.买入状态-(持有)// 1.保持卖出股票的状态// 2.今天…

JVM运行时数据区——堆

文章目录 1、堆的核心概述1.1、JVM实例与堆内存的对应关系1.2、堆与栈的关系1.3、JVM堆空间划分 2、设置堆内存大小与内存溢出2.1、设置堆内存大小2.2、内存溢出案例 3、新生代与老年代4、图解对象分配过程5、Minor GC、Major GC、Full GC5.1、GC的分类5.2、分代式GC策略的触发…

Sora:AI视频模型的无限可能与挑战

随着人工智能技术的突飞猛进&#xff0c;AI视频模型已成为科技领域的新焦点。OpenAI推出的AI视频模型Sora&#xff0c;凭借其卓越的技术性能和前瞻性&#xff0c;为AI视频领域的发展揭开了新的篇章。本文将从技术解析、应用场景、未来展望、伦理与创意以及用户体验与互动五个方…

【python基础学习09课_装饰器、模块、文件】

一、项目的日志 1、日志意义与级别 1、日志的意义&#xff1a;项目的日志 -- 开发编写的&#xff0c;日志记录 -- 测试就是去查看日志信息&#xff08;为了协助我们进行问题的定位&#xff09; 可以根据日志&#xff0c;看是哪个应用的哪台机器&#xff0c;出现了什么问题&…

Scala 之舞:林浩然与杨凌芸的 IDEA 冒险

Scala 之舞&#xff1a;林浩然与杨凌芸的 IDEA 冒险 The Dance of Scala: The IDEA Adventure of Lin Haoran and Yang Lingyun 在那个阳光明媚的日子里&#xff0c;林浩然如同一位英勇的探险家&#xff0c;踏入了 Scala 的 IntelliJ IDEA 开发环境的奇妙领域&#xff0c;他带着…

day7 字符数组

1&#xff1a;输入一个字符串&#xff0c;实现单词逆置 输入:"good good study" 输出&#xff1a;"study good good" 6 //单词逆置7 // good good study8 // study good good9 10 //整体逆置11 char str[50]"good good stu…

springboot集成logback打印彩色日志

一、logback介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站&#xff1a; logback.qos.ch。它当前分为以下三个模块&#xff1a; logback-core&#xff1a;其它两个模块的基础模块。logback-classic&#xff1a;它是log4j的一个改良版本&#xff0c;同时它完整实…

C++基于多设计模式下的同步异步日志系统day6

C基于多设计模式下的同步&异步日志系统day6 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&am…

云手机运行在云端?安全性有保障吗

随着云计算技术的不断发展&#xff0c;云手机作为一种新兴的移动终端形态&#xff0c;逐渐成为人们关注的焦点。然而&#xff0c;对于许多人来说&#xff0c;云手机 是一个相对陌生的概念&#xff0c;安全性成为了他们最为关心的问题之一。本文将就云手机运行在云端的特点以及其…

JWT身份验证

在实际项目中一般会使用jwt鉴权方式。 JWT知识点 jwt&#xff0c;全称json web token &#xff0c;JSON Web令牌是一种开放的行业标准RFC 7519方法&#xff0c;用于在两方安全地表示声明。具体网上有许多文章介绍&#xff0c;这里做简单的使用。 1.数据结构 JSON Web Token…

#include<ros/ros.h>头文件报错

快捷键 ctrl shift B 调用编译&#xff0c;选择:catkin_make:build&#xff09;(要先在vscode上添加扩展&#xff1a;ros) 可以点击配置设置为默认&#xff0c;修改.vscode/tasks.json 文件 修改.vscode/tasks.json 文件&#xff0c;否则ros.h头文件会报错 内容修改为以下内…

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:拖拽控制)

设置组件是否可以响应拖拽事件。 说明&#xff1a; 从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 ArkUI框架对以下组件实现了默认的拖拽能力&#xff0c;支持对数据的拖出或拖入响应&#xff0c;开发者只需要将这些组件…