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

news2024/11/17 19:43:51

前排提示:提出一个可以让 [^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.coloradmin.cn/o/1492551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity使用UnityWebRequest读取音频长度不对的解决方法

在开发的过程中碰到这样一个问题&#xff0c;有的音频文件通过UnityWebRequest读取出来后&#xff0c;AudioClip的Length会不对&#xff0c;比如本身有7秒&#xff0c;读出来只有3秒。代码如下&#xff1a; IEnumerator TestEnumerator() {UnityWebRequest www UnityWebReque…

BERT学习【BERT的例子以及作用】

一、case 1.case1 多输入单输出。通过输入一个句子&#xff08;sequence&#xff09;&#xff0c;然后输出一个句子的分类&#xff08;这个句子是正向还是负向&#xff09;。将句子输入BERT&#xff0c;然后通过softmax输出分类。 2.case2 多输入多输出。输入一个句子&…

专业的安全数据交换系统,如何进行网间数据安全交换?

网络隔离是企业网络安全管理的重要组成部分&#xff0c;它有助于提高网络的整体安全性&#xff0c;保护企业资产和客户数据&#xff0c;同时满足法规合规要求。很多企业为了防止内部核心数据泄露&#xff0c;都实施了网络隔离&#xff0c;比如划分成内网、外网&#xff0c;有的…

安装R语言,完整教程

安装R语言&#xff0c;完整教程 介绍特点 应用场景和用户对象安装教程下载地址安装打开软件 介绍 R语言是一种用于统计分析和数据可视化的编程语言&#xff0c;广泛应用于学术研究、数据科学、商业分析等领域。它由新西兰奥克兰大学的Ross Ihaka和Robert Gentleman于1995年开发…

Ps:色阶

色阶 Levels命令用于改善图像的对比度和调整亮度/暗度平衡&#xff0c;通过操作图像的直方图来精细调整图像的阴影、中间调和高光。 Ps菜单&#xff1a;图像/调整/色阶 Adjustments/Levels 快捷键&#xff1a;Ctrl L Ps菜单&#xff1a;图层/新建调整图层/色阶 New Adjustment…

Acwing 周赛145 解题报告 | 珂学家 | 贪心构造

前言 整体评价 VP了这场比赛&#xff0c;偏简单吧。T3是道贪心构造题&#xff0c;卡语言&#xff0c;因为题和数都是从CF拿来的。 A. 平均成绩 思路: 模拟 这边有个小技巧&#xff0c;就是规避浮点运算。 可以左右侧都乘以3。 #include <bits/stdc.h>using namespac…

稀碎从零算法笔记Day9-LeetCode:最后一个单词的长度

题型&#xff1a;字符串、反转字符串 链接:58. 最后一个单词的长度 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述&#xff08;红字为笔者添加&#xff09; 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔…

Linux Ubuntu 部署SVN

最近需要在ubuntu server上部署一个svn&#xff0c;记录 不需要特定版本 如果不需要特定版本&#xff0c;这样安装就是最简单的 sudo apt update然后开始安装 sudo apt install subversion等到安装完成之后执行查看版本命令&#xff0c;如果正常输出那就没问题了 svnadmin …

Rust 开发的高性能 Python 包管理工具,可替换 pip、pip-tools 和 virtualenv

最近&#xff0c;我在 Python 潮流周刊 中分享了一个超级火爆的项目&#xff0c;这还不到一个月&#xff0c;它在 Github 上已经拿下了 8K star 的亮眼成绩&#xff0c;可见其受欢迎程度极高&#xff01;国内还未见有更多消息&#xff0c;我趁着周末把一篇官方博客翻译出来了&a…

绝地求生:调整不同血雾颜色效果,显著提升抽靶击中时的反馈效果

嗨&#xff0c;我是闲游盒~ 有很多玩家在玩游戏时&#xff0c;游戏设置方面默认的偏多&#xff0c;其中就有击中敌人时&#xff0c;血雾颜色呈暗绿色&#xff0c;导致抽靶时不好判断是否击中敌人。 在补枪方面也有很大影响&#xff0c;尤其在植被茂密的地形中。 你看得出来被击…

EPSON RA8000CE (RTC模块)压电侠

RA8000CE是一个集成了32.768 kHz数字温度补偿晶体振荡器(DTCXO)的RTC模块。它包括各种功能&#xff0c;如具有闰年校正的秒到年时钟/日历&#xff0c;时间警报&#xff0c;唤醒计时器&#xff0c;时间更新中断&#xff0c;时钟输出和时间戳功能&#xff0c;可以在外部或内部事件…

【Photoshop2020版本】零基础笔记(一)

哈喽大家好~最近博客内容换方向了哈哈哈~换成“实用版”了。 今天给大家带来的是 PS 相关内容 其实我也是刚学PS&#xff0c;所以想着自己做笔记还不如发布出去&#xff0c;让大家都能看到&#xff0c;有兴趣的伙伴们&#xff0c;可以跟着我的笔记一块学习&#xff0c;这个专…

出现身份验证错误,无法连接到本地安全机构 顺利解决这个问题希望能帮助大家

出现身份验证错误&#xff0c;无法连接到本地安全机构&#xff0c;远程计算机&#xff1a;XX&#xff0c;这可能是由于密码过期&#xff0c;如果密码已过期请更新密码。 我们可以在系统属性中对远程进行设置&#xff0c;以解决远程桌面无法连接到本地安全机构这一问题。 步骤…

HTML常用的五种标签,太棒了

form表单细节 一、表单 1.表单 标签用于为用户输入创建 HTML 表单 2.表单能够包含 input 元素&#xff0c;比如文本字段、复选框、单选框、提交按钮等等。 3.表单还可以包含 menus、textarea、fieldset、legend 和 label 元素。 4.表单用于向服务器传输数据。 二、表单form 的…

大语言模型在科技研发与创新中的角色在快速变化

在技术研发与创新中&#xff0c;比如在软件开发、编程工具、科技论文撰写等方面&#xff0c;大语言模型可以辅助工程师和技术专家进行快速的知识检索、代码生成、技术文档编写等工作。在当今的软件工程和研发领域&#xff0c;尤其是随着大语言模型技术的快速发展&#xff0c;它…

算法46:动态规划专练(力扣198: 打家劫舍 力扣740:删除并获取点数)

打家劫舍问题&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定…

flutter框架优缺点,最新Android大厂高频面试题

部分面试常问的面试专题 一、Java篇 1.多线程并发&#xff1b; sleep 和 wait 区别join 的用法线程同步&#xff1a;synchronized 关键字等线程通信线程池手写死锁 2.Java 中的引用方式&#xff0c;及各自的使用场景 3.HashMap 的源码 4.GC(垃圾回收)是什么&#xff1f;如何…

运维随录实战(8)之搭建数据库

docker搭建mysql5.7 如果出现no matching manifest for windows/amd64 10.0.18363 in the manifest list entries错误,则将docker desktop的settings->Docker Engine中的experimental值设为true。 touch : 无法将“touch”项识别为 cmdlet、函数、脚本文件或可运行程序的…

is not valid JSON at JSON.parse

在后台读取一个文件里的JSON数据&#xff0c;转换成字符串返回给前端&#xff0c;前端使用JSON.parse转换JSON报错。在将JSON校验和压缩后发现前端还是转换失败。在返回结果的时候可以看见一个小红点 最后排查&#xff0c;不带BOM的识别是Java遗留的一个bug。 解决方案&#…

【排序】详解插入排序

一、思想 插入排序是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。具体步骤如下&#xff0c;将数组下标为0的元素视为已经排序的部分&#xff0c;从1开始遍历数组&#xff0c;在遍历的过程中当前元素从…