2024年11月

今天和大家享一些关于枚举操作相关的常用扩展方法。

我们平时用的比较多的是正常枚举,同时还有加[Flags]特性的位标志枚举,因此以下所有扩展方法同时适用正常枚举以及位标志枚举。

我们首先定义两种枚举用于下面扩展方法测试适用,代码如下:

//正常枚举
internal enum StatusEnum
{
    [Description("正常")]
    Normal = 0,
    [Description("待机")]
    Standby = 1,
    [Description("离线")]
    Offline = 2,
    Online = 3,
}
//位标志枚举
[Flags]
internal enum TypeFlagsEnum
{
    [Description("Http协议")]
    Http = 1,
    [Description("Udp协议")]
    Udp = 2,
    [Description("Http协议,Udp协议")]
    HttpAndUdp = 3,
    [Description("Tcp协议")]
    Tcp = 4,
}

01
、根据枚举名称转换成枚举

该方法接收枚举名称字符串,并转为对应枚举,转换失败则返回空。

首先会校验字符串是否为整数值类型字符串,如果是则直接返回空,因为枚举名称不可能是整数类型字符串。

然后调用TryParse方法进行转换,具体代码如下:

//根据枚举名称转换成枚举,转换失败则返回空
public static T? ToEnumByName<T>(this string name) where T : struct, Enum
{
    //如果为整数类型字符串,则直接返回空
    if (int.TryParse(name, out _))
    {
        return default;
    }
    //转换成功则返回结果,否则返回空
    if (Enum.TryParse<T>(name, out var result))
    {
        return result;
    }
    return default;
}

下面我们对其进行详细的单元测试,分别针对正常枚举和位标志枚举两种情况测试,具体用例如下:

(1) 正常枚举名称字符串,成功转换成枚举;

(2) 不存在的枚举名称字符串,返回空;

(3) 整数类型的字符串,返回空;

(4) 正常位标志枚举名称字符串,成功转换成枚举;

(5) 不存在的位标志枚举名称字符串,返回空;

(6) 正常的位标志枚举名称组合字符串,成功转换成枚举;

(7) 不存在的位标志枚举名称组合字符串,返回空;

位标志枚举名称组合字符串是指两个枚举项组合的情况,这也是位标志枚举特色,不了解位标志枚举的可以自行先补充一下相关知识点。

具体代码如下:

[Fact]
public void ToEnumByName()
{
    //正常枚举名称字符串,成功转换成枚举
    var status = "Standby".ToEnumByName<StatusEnum>();
    Assert.Equal(StatusEnum.Standby, status);
    //不存在的枚举名称字符串,返回空
    var isStatusNull = "StandbyNull".ToEnumByName<StatusEnum>();
    Assert.Null(isStatusNull);
    //整数类型的字符串,返回空
    var isStatusNullInt = "3".ToEnumByName<StatusEnum>();
    Assert.Null(isStatusNullInt);
    //正常位标志枚举名称字符串,成功转换成枚举
    var flags = "HttpAndUdp".ToEnumByName<TypeFlagsEnum>();
    Assert.Equal(TypeFlagsEnum.HttpAndUdp, flags);
    //不存在的位标志枚举名称字符串,返回空
    var isFlagsNull = "HttpAndUdpNull".ToEnumByName<TypeFlagsEnum>();
    Assert.Null(isFlagsNull);
    //正常的位标志枚举名称组合字符串,成功转换成枚举
    var flagsGroup = "Http,Tcp".ToEnumByName<TypeFlagsEnum>();
    Assert.Equal(TypeFlagsEnum.Http | TypeFlagsEnum.Tcp, flagsGroup);
    //不存在的位标志枚举名称组合字符串,返回空
    var isFlagsGroupNull = "Http,Tcp,Null".ToEnumByName<TypeFlagsEnum>();
    Assert.Null(isFlagsGroupNull);
}

02
、根据枚举名称转换成枚举或默认值

该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举值,具体代码如下:

//根据枚举名称转换成枚举,转换失败则返回默认枚举
public static T ToEnumOrDefaultByName<T>(this string name, T defaultValue) where T : struct, Enum
{
    //调用根据枚举名称转换成枚举方法
    var result = name.ToEnumByName<T>();
    if (result.HasValue)
    {
        return result.Value;
    }
    //转换失败则返回默认值
    return defaultValue;
}

因为该方法调用了上一个方法,因此我们就简单测试一下,转换成功返回正确的值,转换失败则返回默认值,具体代码如下:

[Fact]
public void ToEnumOrDefaultByName()
{
    //正常枚举名称字符串,成功转换成枚举
    var status = "Standby".ToEnumOrDefaultByName(StatusEnum.Normal);
    Assert.Equal(StatusEnum.Standby, status);
    //不存在的枚举名称字符串,返回指定默认值
    var statusDefault = "StandbyNull".ToEnumOrDefaultByName(StatusEnum.Standby);
    Assert.Equal(StatusEnum.Standby, statusDefault);
}

03
、根据枚举描述转换成枚举

该方法接收枚举描述字符串,并转为对应枚举,转换失败则返回空,其中如果枚举项没有描述则以枚举名称代替,具体代码如下:

//根据枚举描述转换成枚举,转换失败返回空
public static T? ToEnumByDesc<T>(this string description) where T : struct, Enum
{
    //首先获取枚举所有项
    foreach (Enum value in Enum.GetValues(typeof(T)))
    {
        //取枚举项描述与目标描述相比较,相同则返回该枚举项
        if (value.ToEnumDesc() == description)
        {
            return (T)value;
        }
    }
    //未查到匹配描述则返回默认值
    return default;
}

其中ToEnumDesc方法下文会详细讲解。

我们针对该方法进行以下五种情况进行单元测试:

(1) 正常枚举描述字符串,成功转换成枚举;

(2) 如果枚举项没有枚举描述,则枚举名称字符串,成功转换成枚举;

(3) 不存在的枚举描述字符串,返回空;

(4) 正常位标志枚举描述字符串,成功转换成枚举;

(5) 不存在的位标志枚举描述字符串转换,返回空;

具体代码如下:

[Fact]
public void ToEnumByDescription()
{
    //正常枚举描述字符串,成功转换成枚举
    var status = "待机".ToEnumByDesc<StatusEnum>();
    Assert.Equal(StatusEnum.Standby, status);
    //如果枚举项没有枚举描述,则枚举名称字符串,成功转换成枚举
    var statusNotDesc = "Online".ToEnumByDesc<StatusEnum>();
    Assert.Equal(StatusEnum.Online, statusNotDesc);
    //不存在的枚举描述字符串,返回空
    var isStatusNull = "待机无".ToEnumByDesc<StatusEnum>();
    Assert.Null(isStatusNull);
    //正常位标志枚举描述字符串,成功转换成枚举
    var flags = "Http协议,Udp协议".ToEnumByDesc<TypeFlagsEnum>();
    Assert.Equal(TypeFlagsEnum.HttpAndUdp, flags);
    //不存在的位标志枚举描述字符串转换,返回空
    var isFlagsNull = "Http协议Udp协议".ToEnumByDesc<TypeFlagsEnum>();
    Assert.Null(isFlagsNull);
}

04
、根据枚举描述转换成枚举或默认值

该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举值,其中如果枚举项没有描述则以枚举名称代替,具体代码如下:

//根据枚举描述转换成枚举,转换失败返回默认枚举
public static T? ToEnumOrDefaultByDesc<T>(this string description, T defaultValue) where T : struct, Enum
{
    //调用根据枚举描述转换成枚举方法
    var result = description.ToEnumByDesc<T>();
    if (result.HasValue)
    {
        return result.Value;
    }
    //未查到匹配描述则返回默认值
    return defaultValue;
}

同样的我们进行简单的单元测试:

[Fact]
public void ToEnumOrDefaultByDesc()
{
    //正常枚举描述字符串,成功转换成枚举
    var status = "待机".ToEnumOrDefaultByDesc(StatusEnum.Offline);
    Assert.Equal(StatusEnum.Standby, status);
    //不存在的枚举描述字符串,返回指定默认值
    var statusDefault = "待机无".ToEnumOrDefaultByDesc(StatusEnum.Offline);
    Assert.Equal(StatusEnum.Offline, statusDefault);
}

05
、根据枚举名称转换成枚举值

该方法接收枚举名字字符串,并转为对应枚举值,转换失败则返回空,具体代码如下:

//根据枚举名称转换成枚举值,转换失败则返回空
public static int? ToEnumValueByName<T>(this string name) where T : struct, Enum
{
    //调用根据枚举名称转换成枚举方法
    var result = name.ToEnumByName<T>();
    if (result.HasValue)
    {
        return Convert.ToInt32(result.Value);
    }
    //转换失败则返回空
    return default;
}

我们对其进行以下五种情况做详细的单元测试:

(1) 正常枚举名称字符串,成功转换成枚举值;

(2) 不存在的枚举名称字符串,返回空;

(3) 正常位标志枚举名称字符串,成功转换成枚举值;

(4) 正常的位标志枚举名称组合字符串,成功转换成枚举值;

(5) 不存在的位标志枚举Int值转换则返回空;

具体代码如下:

[Fact]
public void ToEnumValueByName()
{
    //正常枚举名称字符串,成功转换成枚举值
    var status = "Standby".ToEnumValueByName<StatusEnum>();
    Assert.Equal(1, status);
    //不存在的枚举名称字符串,返回空
    var isStatusNull = "StandbyNull".ToEnumValueByName<StatusEnum>();
    Assert.Null(isStatusNull);
    //正常位标志枚举名称字符串,成功转换成枚举值
    var flags = "HttpAndUdp".ToEnumValueByName<TypeFlagsEnum>();
    Assert.Equal(3, flags);
    //正常的位标志枚举名称组合字符串,成功转换成枚举值
    var flagsGroup = "Http,Udp".ToEnumValueByName<TypeFlagsEnum>();
    Assert.Equal(3, flagsGroup);
    //不存在的位标志枚举名称字符串,返回空
    var isFlagsNull = "HttpUdp".ToEnumValueByName<TypeFlagsEnum>();
    Assert.Null(isFlagsNull);
}

06
、根据枚举名称转换成枚举值或默认值

该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举值,具体代码如下:

//根据枚举名称转换成枚举值,转换失败则返回默认枚举值
public static int ToEnumValueOrDefaultByName<T>(this string name, int defaultValue) where T : struct, Enum
{
    //根据枚举名称转换成枚举值
    var result = name.ToEnumValueByName<T>();
    if (result.HasValue)
    {
        return result.Value;
    }
    //转换失败则返回默认值
    return defaultValue;
}

我们进行简单的单元测试,具体代码如下:

[Fact]
public void ToEnumValueOrDefaultByName()
{
    //正常枚举名称字符串,成功转换成枚举值
    var status = "Standby".ToEnumValueOrDefaultByName<StatusEnum>(2);
    Assert.Equal(1, status);
    //不存在的枚举名称字符串,返回指定默认值
    var statusDefault = "StandbyNull".ToEnumValueOrDefaultByName<StatusEnum>(2);
    Assert.Equal(2, statusDefault);
}

07
、根据枚举名称转换成枚举描述

该方法接收枚举名字字符串,并转为对应枚举描述,转换失败则返回空,其中如果枚举项没有描述则以枚举名称代替,具体代码如下:

//根据枚举名称转换成枚举描述,转换失败则返回空
public static string? ToEnumDescByName<T>(this string name) where T : struct, Enum
{
    //调用根据枚举名称转换成枚举方法
    var result = name.ToEnumByName<T>();
    if (result.HasValue)
    {
        //转为枚举描述
        return result.Value.ToEnumDesc();
    }
    //转换失败则返回空
    return default;
}

因为该方法内部都是调用现有方法,因此做个简单单元测试,具体代码如下:

[Fact]
public void ToEnumDescByName()
{
    //正常位标志枚举名称字符串,成功转换成枚举描述
    var flags = "HttpAndUdp".ToEnumDescByName<TypeFlagsEnum>();
    Assert.Equal("Http协议,Udp协议", flags);
    //正常的位标志枚举名称组合字符串,组合项存在,成功转换成枚举描述
    var flagsGroup = "Http,Udp".ToEnumDescByName<TypeFlagsEnum>();
    Assert.Equal("Http协议,Udp协议", flagsGroup);
    //正常的位标志枚举名称组合字符串,组合项不存在,成功转换成枚举描述
    var flagsGroup1 = "Http,Tcp".ToEnumDescByName<TypeFlagsEnum>();
    Assert.Equal("Http协议,Tcp协议", flagsGroup1);
}

08
、根据枚举名称转换成枚举描述或默认值

该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举描述,具体代码如下:

//根据枚举名称转换成枚举描述,转换失败则返回默认枚举描述
public static string ToEnumDescOrDefaultByName<T>(this string name, string defaultValue) where T : struct, Enum
{
    //调用根据枚举名称转换成枚举描述方法
    var result = name.ToEnumDescByName<T>();
    if (!string.IsNullOrWhiteSpace(result))
    {
        return result;
    }
    //转换失败则返回默认枚举描述
    return defaultValue;
}

做个简单单元测试,具体代码如下:

[Fact]
public void ToEnumDescOrDefaultByName()
{
    //正常枚举名称字符串,成功转换成枚举描述
    var status = "Standby".ToEnumDescOrDefaultByName<StatusEnum>("测试");
    Assert.Equal("待机", status);
    //不存在的枚举名称字符串,返回指定默认枚举描述
    var statusDefault = "StandbyNull".ToEnumDescOrDefaultByName<StatusEnum>("测试");
    Assert.Equal("测试", statusDefault);
}

稍晚些时候我会把库上传至Nuget,大家可以直接使用Ideal.Core.Common。


:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。
https://gitee.com/hugogoos/Ideal

最近研究了下Surface pro 11,记录下相关参数,在罗列这些参数过程中,自己科普了很多硬件知识。。。之前是Windows应用开发为主,后面尽量多了解硬件知识、开拓视野,期望自己在目前主要从事的交互大屏行业方向做的更好。

矩阵我以表格列出来,可能不够细,大家作个参考吧

模块 技术项 参数 备注
处理器 型号 Snapdragon X Elite(X1E-80-100) 骁龙ARM芯片
核数 12核
缓存 42M 三级缓存
主频 3.4GHz
睿频 4.0GHz 最大睿频,双核增强
NPU 型号 Qualcomm Hexagon 骁龙CPU集成
算力 45Tops 万亿次
显卡 型号 Qualcomm Adreno X1 GPU 骁龙CPU核显
算力 3.8TFLOPS
内存 容量 16/32/64GB
DDR类型 LPDDR5x 低功耗双倍数据率内存,适合注重续航的设备
DDR速率 8448MT/s
硬盘 容量 256G/512G/1T
SSD类型 PCIE 4.0 硬盘接口
显示屏 屏幕尺寸 13寸
屏幕类型 OLED 柔性屏
分辨率 2880*1920(267PPI) PPI才是像素密度
屏幕比例 3:2
对比度 1000000:1
刷新率 120Hz 动态刷新率
颜色配置 sRGB 和 Vivid
峰会亮度 600nits
触控方式 10点触控 一般触摸显示屏是20点
琉璃造型 康宁大猩猩5 Corning® Gorilla® Glass 5
杜比视界 支持 Dolby Vision IQ™20
续航 电池容量 53Wh
电源接口 微软款电源口 一般我们用typec
播放时间 支持 14 小时的本地视频播放
支持 10 小时的网页浏览
尺寸 长宽高 287*208*9.3mm
重量 平板 895g
键盘 340g
视频/摄像头 前置摄像头 300万
四倍高清超广角摄像头
Windows Hello 人脸身份验证摄像头
后置摄像头 1050万
录音室功能 背景效果:背景模糊
眼神交流:眼神校正
自动取景:检测摄像头视野中的人物,并进行裁剪/变焦,使其保持在取景框内。
人像光效:提高在光线不足环境下视频中人像的光效质量,消除强屏幕光反射。
创意滤镜:用于添加一些趣味效果和眩光的视频滤镜。
需要摄像头支持
扬声器 喇叭 2W立体扬声器
音效 杜比全景声
支持蓝牙LE 音频
杜比音效,需要扬声器设备支持、以及申请许可
麦克风 语音聚焦功能麦克风*2
解锁 人脸解锁 需要摄像头支持
指纹解锁 指纹、电源键二合一
外部接口 电源 Surface定制充电口
typec 雷电4*2 全功能typec,支持充电
外部按键 音量键
电源键 短按电源键,息屏、息屏唤醒、睡眠唤醒、休眠唤醒
长按电源键3s,提示下滑关闭
长按电源键12s,强制关机
网络 无线网络 Wifi7,Intel BE200 最大传输速率23Gbs
蓝牙 蓝牙5.4
配件 触控笔 手写笔按钮
书写4096级压感
磁吸充电
提笔检测,下方会弹出笔菜单选项
磁吸键盘 支持键盘离线使用、以及热插拔唤醒触摸软键盘
支持键盘唤醒
支持按键亮度、音量、锁屏等系统操作
支持保护盖息屏唤醒
1. 屏幕保护套可以关盖息屏
2. 屏幕保护套可以开盖唤醒
AI PC按键
触摸板,支持1指2指3指4指,同windows原生系统操作
传感器 加速计 可用于重力感应,例如屏幕四个方向自动翻转,支持竖屏模式
硬盘保护
G-sensor,也叫重力传感器
陀螺仪 设备转向检测,用于导航功能等
磁力计 支持磁吸皮套感应,关闭皮套息屏,打开皮套亮屏
环境颜色传感器 颜色自适应、反炫光
光感传感器 亮度自适应
外观 外观材质 阳极氧化铝
外观配色 蓝宝石、沙漫金、典雅黑、亮铂金
外观支架 165度铰链
Windows 版本 Win11 24H2 家庭版
安全性 通过 Microsoft Pluton TPM 2.0 增强的安全性
Windows 11 安全核心 PC
Windows Hello 人脸验证功能,具备增强的登录安全性
Microsoft Defender – 更强劲的身份和隐私保护(预装)
安全核心PC:安全启动、Bitlocker 设备加密、Microsoft Defender、Windows Hello 和 TPM 2.0
恢复 1. 联网重新安装当前版本的Windows(应用、文件和设置将保留) - 修复系统损坏/修改
2. 重置此电脑 - 保留个人文件
3. 重置此电脑 - 删除所有文件
3. 高级启动 - 进入WinRE界面
应用 AI PC Copilot 值得一说的是,国内用不了Copilot
OneNote 手写笔专用,提笔可触发下载
Microsoft 白板 手写笔专用,提笔可触发下载

下面是对硬件以及系统参数了解过程中,访问了一些网站,有需要的可以点击查看

Surface pro 11 :

Surface Pro 第11版-微软AI+ PC-Windows 11 AI笔记本-微软官方商城
最下方点击“显示所有Surface pro规格”,可查看详细参数

Surface Pro:由 AI 驱动的商用版二合一平板电脑 | Microsoft Surface

官网也有产品的具体功能及规格参数,比较遗憾的是pro 11并没有,有输出参数文档且比较接近的版本是pro 9:
Surface Pro 9 个功能和规格 - Microsoft 支持

Cpu 骁龙 8 Gen 3 / X Elite:

AI 定义芯片!骁龙 8 Gen 3 / X Elite 平台解读:体验全面提升 - IT之家

高通 Adreno X1 规格揭晓,多款游戏中性能不输英特尔 Arc 核显 - IT之家

Intel CPU P/E核:
12代、13代英特尔 CPU:什么是 P-CORE 和 E-CORE 核? - 知乎

CPU三级缓存:
什么是CPU缓存,玩游戏为什么追求大三缓 - 知乎

CPU主频与睿频:

主频是处理器正常工作时的性能水准,它表示处理器内核在正常工作情况下所能达到的性能水平。而最大睿频则是指处理器自动睿频能够达到的最高频率。这个数值更能代表一款处理器的真实实力,因为在实际使用中,处理器往往需要超常发挥才能满足高负载任务的需求。
CPU的主频和睿频哪个重要?为什么?

同频率不同架构的CPU:主频与最大睿频

显卡:
显卡知识:集显、核显、独显、双显有什么区别 - 知乎

NPU :
探索高通骁龙处理器中的Hexagon NPU架构 - 知乎

CPU、GPU、TPU、NPU等到底是什么? - 知乎

什么是 TOPS,为什么它对 AI 很重要? - 知乎

OLED屏幕:
LCD和OLED的屏幕区别和优缺点 - 知乎

内存条

硬件知识:DDR3、DDR4和DDR5内存条有啥区别,看完你就懂了! - 知乎

LPDDR5 vs. DDR5 内存:有什么区别? - 系统极客

存储硬盘

科普讲堂——固态硬盘(SSD)扫盲 - 知乎

分辨率/PPI

关于像素、分辨率、PPI、DPI等概念的分析_ppi和分辨率的区别-CSDN博客

Windows

网卡

什么是WiFi 7?WiFi 7和WiFi 6的区别是什么? - 华为

Typec接口

雷电口和Type-C有区别吗,为什么雷电口长得和Type-c一模一样? - 知乎

触控笔

主动笔+平板电脑_主动笔协议mpp和usi-CSDN博客

加速度计

加速度计应用于笔记本电脑硬盘保护 - MEMS/传感技术 - 电子发烧友网

磁力计

加速计/陀螺仪/磁力计是什么,3轴/6轴/9轴传感器又是什么?_九轴传感器是指重力传感器+陀螺仪+地磁-CSDN博客

环境颜色传感器

Surface 设备上的自适应亮度和对比度 - Microsoft 支持

颜色传感器 - 从零开始认识各种传感器【二十三期】_识别颜色变化传感器-CSDN博客

win11家庭版版本区别
探讨 Windows 客户端版本和功能 - Training | Microsoft Learn

硬件兼容性测试
Windows Hardware Lab Kit | Microsoft Learn

后面可能还会更新。。。

1 PAC

AMR64
提供了
PAC
(
P
ointer
A
uthentication
C
ode)机制。

所谓
PAC
,简单来说就是使用存储在芯片硬件上的「密钥」,一个「上下文」,与「指针地址」进行加密计算,得出一个「签名」,将这个「签名」写入指针的高
bit
上。

image

计算出来的「签名」之所以可以写入指针的高
bit
,是因为在实际使用中,并不是所有指针
64bit
都会被使用,通常高
bit
都会被保留用来做其他用途。

image

如果在程序运行过程中,有人恶意的修改了指针地址,在后面将签名后的指针地址,作用于验证指令,就会报错。

验证过程和计算签名过程一样,只是比较前后计算的签名值是否一样。如果验证成功,签名就会从高
bit
上移除。

image

2 指令语法

PACIBSP

3 指令语义

上面指令对函数返回地址进行
PAC
,也就是对
X30
的值进行
PAC
,或者说对
LR
寄存器的值进行
PAC


PAC
过程中,有
2
种密钥,一种是密钥
A
,另一种是密钥
B

指令
PACIBSP
中的
B
就代表使用密钥
B
,同时
SP
表示「上下文」使用的是
SP
寄存器的值。

下面是一个运行
PACIBSP
后,
X30
的值变化的例子:

// PAC 前
(lldb) p/x $x30
(unsigned long) 0x000000010817f7bc

// PAC 后
(lldb) p/x $x30
(unsigned long) 0x3f57fc010817f7bc

从上面的输出可以看到,
X30
的高
bit

PAC
后发生了变化。

4 同类指令

4.1 PACIB

PACIB <Xd>, <Xn|SP>

密钥
: 密钥
B

上下文
:
Xn
寄存器的值或者
SP
寄存器的值。

指针地址
:
Xd
寄存器的值

4.2 PACIB1716

PACIB1716

密钥
: 密钥
B

上下文
: 寄存器
X16
的值。

指针地址
: 寄存器
X17
的值。

4.3 PACIBZ

PACIBZ

密钥
: 密钥
B

上下文
:
0

指针地址
:
X30
寄存器或者说
LR
寄存器的值。

4.4 PACIZB

PACIZB <Xd>

密钥
: 密钥
B

上下文
:
0

指针地址
: 寄存器
Xd
的值。

4.5 密钥 A 指令

指令
PACIASP
PACIA
PACIA1716
PACIAZ
PACIZA
除了使用密钥
A
进行
PAC
之外,其它都与对应的
PACIB*
指令一样。

Microsoft 今天正式发布了 .NET 9,这是迄今为止最高效、最现代、最安全、最智能、性能最高的 .NET 版本。这是来自世界各地的数千名开发人员又一年努力的结果。此新版本包括数千项性能、安全性和功能改进。您将发现整个 .NET 堆栈中从编程语言、开发人员工具和工作负载的全面增强功能,使您能够使用统一平台进行构建,并轻松地将 AI 注入您的应用程序。

Overview of .NET with workloads, tools, ecosystem, and OS

.NET 9 的下载以及 Visual Studio 2022 的更新和适用于 Visual Studio Code 的 C# 开发工具包现已推出。

.NET开发团队在
.NET Conf
[3] 直播发布活动的主题演讲和后续会议中重点介绍了 .NET Aspire 和 AI。

image

.NET Aspire 是官方 .NET 9
发布公告
[4]的前沿和中心,是讨论的第一个组件,.NET Aspire 是一组强大的工具、模板和包,用于无缝开发可观察的生产就绪应用程序,自 .NET Aspire 首次发布以来,我们仅六个月时间,我们已经对堆栈的所有部分进行了改进,从遥测和指标仪表板中的新功能到更简化的云应用程序部署。很高兴看到 .NET Aspire 在所有类型的应用程序中被采用,并看到社区接受适用于其方案的集成和工具。

image

.NET Aspire 9.0 的新功能在于它引入了开发者最需要的功能来简化应用程序开发。用户现在可以从控制面板启动和停止资源,在调试会话之间保持容器持久性,并利用包括 WaitFor 在内的新 API 来改进资源管理。与 OpenAI、Ollama、Milvus 等的新集成增强了灵活性。新增了
.NET Aspire Community Toolkit
[5],这是一个开源的集成和扩展集合,用于使用 .NET Aspire 进行开发。

在 .NET Aspire 讨论之后,团队在直播和
发布公告
[4]帖子中继续讨论 .NET 9 中的 AI。Microsoft 的 Maria Naggaga 说:“从使用 Microsoft Copilot 的使用者应用程序到使用 GitHub Copilot 的开发人员应用程序,.NET 是这些顶级 AI 体验的核心。今年,我们看到整个行业的团队利用 .NET 构建了令人惊叹的 AI 解决方案”。

以下是 .NET 9 中 AI 新增功能的要点摘要:

  • 扩展的 AI 生态系统

AI ecosystem overview for .NET showing libraries and components

  • 新的学习材料和样例
  • 简化了与 .NET 生态系统的集成
  • 与合作伙伴合作,构建充满活力的 AI 社区
  • 改进了 AI 解决方案到云的部署
  • 适用于 .NET 的 AI 构建块:
    Microsoft 引入了新的抽象来简化 AI,包括:

      Diagram explaining how AI extensions work

    • Microsoft.Extensions.AI 和 Microsoft.Extensions.VectorData:它们为与 AI 服务交互提供了统一的 C# 抽象层,包括:
      • 小语言模型和大语言模型(SLM 和 LLM)
      • 嵌入
      • 矢量存储
      • 中间件
    • 改进了 Microsoft.ML.Tokenizers 中的分词器支持:
      • 针对常用模型系列(GPT、Llama、Phi、Bert)的增强标记化
      • 新增了对分词算法(字节级 BPE、SentencePiece、WordPiece)的支持
    • Tensor<T> 增强功能:
      • 表示多维数据的新类型
      • 简化库之间的互操作性
      • 改进了应用操作
  • AI 集成合作伙伴关系:
    .NET 9 包括与各种 AI 合作伙伴的协作,为开发人员提供强大的产品/服务,包括:
    • Azure
    • OpenAI
    • LlamaIndex
    • Qdrant
    • Pinecone
    • Milvus
    • AutoGen
    • Ollamasharp
    • ONNX runtime
  • 智能组件生态系统:
    Microsoft 表示,它与社区和控制供应商合作伙伴合作,构建了一个智能组件生态系统,从而可以更轻松地将注入 AI 的控件集成到 .NET 应用程序中
  • GitHub Copilot 增强功能:
    虽然严格来说不是 .NET 9 的一部分,但该公告强调了为 .NET 开发人员改进的 GitHub Copilot 集成,包括:
    • 用于调试的 AI 智能变量检查
    • AI 驱动的 IEnumerable 可视化工具
    • 改进的代码修复功能
    • 增强的 C# AI 补全
    • 协助调试失败的测试

该发布公告继续讨论了 Blazor、.NET MAUI 和其他属性中的新增功能,并提供了更深入地探讨新增功能的链接

以及更多内容,还有更多博客文章和指南即将推出。


相关链接:


ServiceMesh系列

1 什么是流量染色

在复杂的生产场景中,经常会有同一个服务中,存在多个版本长期共存的需求。为了让不同的用户在不一样的版本中使用,就需要对用户的请求进行采样和染色,打上不同的标识。
这样的目的有几个:

  1. 支撑分级发布,避免全量发布时可能遇到的大规模风险,如系统崩溃、用户流失。
  2. 支持染色实验,让部分人优先体验新版本或者实验功能
  3. QA的线上问题分析、验证、调试,甚至压测都可以放在染色部署区域去做,因为是强隔离模式,可以避免对线上其他用户的影响

使用Service Mesh的流量染色能力,可以在单个服务中根据特征值进行多元版本流量分发。特别是链路繁琐的巨型网格中,能够管理长达10个以上的链路分流调度,这个能力显得非常重要。常见的 Canary Release(金丝雀)、ABTesting、Diversified Version(多版本分流),都是基于此类算法实现。这边介绍在无侵入业务的情况下,Mesh如何实现流量染色。

1. Canary Release
image

2. Diversified Version
image

3. Diversified Version
image

2 Mesh使用标签特性进行染色

Mesh如果想要实现流量染色,需要具备以下几个条件:

  • 请求的流量中,需要附带某些特征,如流量的请求的Header、Cookies、queryParams等,它们带有某些信息。
  • 部署多版本服务
    • 部署在kubernetes上的服务(svc)的实例(pod)需要接入Mesh,并打上版本标签
    • 或者创建不同的服务(svc),后面把流量引入到这个新的服务上去
  • 在Mesh平台上对应的服务中配上策略:当请求的流量带有某些特征(如header中带有UserID=12345678)时,流量路由到对应标签(如 version = v1.7 )的服务实例上。
  • 不符合条件的路由则默认走到默认版本中(如 version = default)。

所以,Mesh的染色本质上是通过在流量中携带一些特征(如流量的请求的Header、Cookies、queryParams等),而Mesh会根据这些请求的特征进行路由匹配,转发到对应的带有某些特征的服务实例上。
未匹配成功的流量则走到默认版本中,从而实现多个版本和跟默认版本的业务隔离的目标。

image

2.1 Mesh 染色流转原理

2.1.1 Istio支持的策略模型

即Istio支持的流量特征包括uri、scheme、method、headers、queryParams等条件,可以根据这些特征进行路由转发:
image

image

完整参考官方文档:
https://istio.io/latest/docs/reference/config/networking/virtual-service/

2.1.2 流量转发实现

基于上述的策略模型,如果你想配置如下:请求的header 带有 username=brand 或者 dep=A1025 的时候,将流量转发到服务的v1版本,否着转发到default版本。
则策略代码如下:

# 说明:VirtualService 流量染色,根据不同的条件将流量发往不同特征的版本中,假设这边有default、v1、v2 版本
apiVersion: networking.istio.io/beta
kind: VirtualService
metadata:
  name: router-test-vs
spec:
  hosts:
  - router-test-vs  # 调度router-test服务的流量
exportTo:
- "."
http:  # 加各种路由条件,比如匹配人员、所属部门进行路由
- match  # 用户匹配 brand,部门匹配 A1025 时
  - headers:
    username:
      exact: brand
  - headers:
    department:
      exact: A1025
  route:
    destination:
    # todo 匹配条件的流量路由到对应的服务上,比如ServiceA-V1
route: 
  destination:
  # todo 不匹配条件的流量路由到其他服务上,比如ServiceA-V2

3 总结

本文介绍了在Mesh场景下如何使用流量染色,来对不同特征的流量进行分发的实现过程。流量染色在我们实际的生产环境中可以有很多收益和价值:

  1. 支撑分级发布,避免全量发布时出现问题
  2. 支持染色实验,让部分人进入实验环境
  3. QA的线上问题分析、验证、调试,甚至压测