提高 C# 的生产力:C# 13 更新完全指南

打印 上一主题 下一主题

主题 507|帖子 507|积分 1521

前言


预计在 2024 年 11 月,C# 13 将与 .NET 9 一起正式发布。今年的 C# 更新紧张会合在 ref struct 上进行了许多改进,并添加了许多有助于进一步提高生产力的便利功能。

本文将先容预计将在 C# 13 中添加的功能。

注意:现在 C# 13 还未正式发布,因此以下内容大概会发生变化。

在迭代器和异步方法中使用 ref 和 ref struct


在使用 C# 进行编程时,你是否经常使用 ref 变量和 Span 等 ref struct 范例?然而,这些不能在迭代器和异步方法中使用,于是必须使用局部函数等来制止在迭代器和异步方法中直接使用 ref 变量 ref struct 范例,这非常不方便。

这个缺点在 C# 13 中得到了改善,现在迭代器和异步方法也可以使用 ref 和 ref struct 了!

在迭代器中使用 ref 和 ref struct 的例子:
  1. IEnumerable<float> GetFloatNumberFromIntArray(int[] array)
  2. {
  3.     for (int i = 0; i < array.Length; i++)
  4.     {
  5.         Span<int> span = array.AsSpan();
  6.         // 进行一些处理...
  7.         ref float v = ref Unsafe.As<int, float>(ref array[i]);
  8.         yield return v;
  9.     }
  10. }
复制代码

在异步方法中使用 ref struct 的例子:
  1. async Task ProcessDataAsync(int[] array)
  2. {
  3.     Span<int> span = array.AsSpan();
  4.     // 进行一些处理...
  5.     ref int element = ref span[42];
  6.     element++;
  7.     await Task.Yield();
  8. }
复制代码

为了展示功能,我使用了不适当且暗昧不清的“一些处置惩罚”,不过紧张的是现在可以使用 ref 和 ref struct 了!

但是,有一点需要注意,ref 变量和 ref struct 范例的变量不能超出 yield 和 await 的边界使用。比方,以下示例将导致编译错误。
  1. async Task ProcessDataAsync(int[] array)
  2. {
  3.     Span<int> span = array.AsSpan();
  4.     // 进行一些处理...
  5.     ref int element = ref span[42];
  6.     element++;
  7.     await Task.Yield();
  8.     element++; // 错误:对 element 的访问超出了 await 的边界
  9. }
复制代码

固然我们已经说到这里,但我想大概有人会疑惑,到底 ref 和 ref struct 是什么,所以我稍微表明一下。

在 C# 中,可以使用 ref 来获取变量的引用。如许,就可以通过引用来更改原始变量。以下是一个例子:
  1. void Swap(ref int a, ref int b) // ref 表示引用
  2. {
  3.     int temp = a;
  4.     a = b;
  5.     b = temp; // 到这里,a 和 b 已经交换了
  6. }
  7. int x = 1;
  8. int y = 2;
  9. Swap(ref x, ref y); // 获取 x 和 y 的引用,调用 Swap 来交换 x 和 y
复制代码

另一方面,ref struct 是用于界说只能存在于堆栈上的值范例的。这是为了制止垃圾网络的开销。然而,由于 ref struct 只能存在于堆栈上,所以在 C# 13 之前,它不能在迭代器和异步方法等地方使用。

顺便一提,ref struct 之所以带有 ref,是由于 ref struct 的实例只能存在于堆栈上,其遵循的生命周期规则与 ref 变量相同。

allows ref struct 泛型约束


在从前,ref struct 不能作为泛型范例参数使用,因此,考虑到代码的可重用性,引入了泛型,但最终 ref struct 不能使用,必须为 Span 或 ReadOnlySpan 重新编写相同的处置惩罚,于是就很麻烦。

在 C# 13 中,泛型范例也可以使用 ref struct 了:
  1. using System;
  2. using System.Numerics;
  3. Process([1, 2, 3, 4], Sum); // 10
  4. Process([1, 2, 3, 4], Multiply); // 24
  5. T Process<T>(ReadOnlySpan<T> span, Func<ReadOnlySpan<T>, T> method)
  6. {
  7.     return method(span);
  8. }
  9. T Sum<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
  10. {
  11.     T result = T.Zero;
  12.     foreach (T value in span)
  13.     {
  14.         result += value;
  15.     }
  16.     return result;
  17. }
  18. T Multiply<T>(ReadOnlySpan<T> span) where T : INumberBase<T>
  19. {
  20.     T result = T.One;
  21.     foreach (T value in span)
  22.     {
  23.         result *= value;
  24.     }
  25.     return result;
  26. }
复制代码

为什么像 ReadOnlySpan<T> 如许的 ref struct 范例可以作为 Func 的范例参数呢?为了调查这个标题,我检察了 .NET 的 源代码,发现 Func 范例的泛型参数是如许界说的:
  1. public delegate TResult Func<in T, out TResult>(T arg)
  2.     where T : allows ref struct
  3.     where TResult : allows ref struct;
复制代码

假如在泛型参数上添加 allow ref struct 约束,那么就可以将 ref struct 范例转达给该参数。
这确实是一个方便的功能。

ref struct 也可以实现接口


在 C# 13 中,ref struct 可以实现接口。

假如将此功能与 allows ref struct 结合使用,那么也可以通过泛型范例转达引用:
  1. using System;
  2. using System.Numerics;
  3. int a = 10;
  4. // 使用 Ref<int> 保存 a 的引用
  5. Ref<int> aRef = new Ref<int>(ref a);
  6. // 传递 Ref<int>
  7. Increase<Ref<int>, int>(aRef);
  8. Console.WriteLine(a); // 11
  9. void Increase<T, U>(T data) where T : IRef<U>, allows ref struct where U : INumberBase<U>
  10. {
  11.     ref U value = ref data.GetRef();
  12.     value++;
  13. }
  14. interface IRef<T>
  15. {
  16.     ref T GetRef();
  17. }
  18. // 为 Ref<T> 这样的 ref struct 实现接口
  19. ref struct Ref<T> : IRef<T>
  20. {
  21.     private ref T _value;
  22.     public Ref(ref T value)
  23.     {
  24.         _value = ref value;
  25.     }
  26.     public ref T GetRef()
  27.     {
  28.         return ref _value;
  29.     }
  30. }
复制代码

如许一来,编写 ref struct 相关的代码就变得更容易了。另外,也能给各种 ref struct 实现的罗列器实现 IEnumerator 之类的接口了。

集合范例和 Span 也可以使用 params


在从前,params 只能用于数组范例,但从 C# 13 开始,它也可以用于其他集合范例和 Span。

params 是一种功能,允许在调用方法时直接指定任意数目的参数。

比方,
  1. Test(1, 2, 3, 4, 5, 6);
  2. void Test(params int[] values) { }
复制代码

如上所示,可以直接指定任意数目的 int 参数。

从 C# 13 开始,除了数组范例外,其他集合范例、Span、ReadOnlySpan 范例以及与集合相关的接口也可以添加 params:
  1. Test(1, 2, 3, 4, 5, 6);
  2. void Test(params ReadOnlySpan<int> values) { }
  3. // 或者
  4. Test(1, 2, 3, 4, 5, 6);
  5. void Test(params List<int> values) { }
  6. // 接口也可以
  7. Test(1, 2, 3, 4, 5, 6);
  8. void Test(params IEnumerable<int> values) { }
复制代码

这也很方便!

field 关键字


在实现 C# 的属性时,经常需要界说一大堆字段,如下所示...
  1. partial class ViewModel : INotifyPropertyChanged
  2. {
  3.     // 定义字段
  4.     private int _myProperty;
  5.     public int MyProperty
  6.     {
  7.         get => _myProperty;
  8.         set
  9.         {
  10.             if (_myProperty != value)
  11.             {
  12.                 _myProperty = value;
  13.                 OnPropertyChanged();
  14.             }
  15.         }
  16.     }
  17. }
复制代码

因此,从 C# 13 开始,field 关键字将派上用场!
  1. partial class ViewModel : INotifyPropertyChanged
  2. {
  3.     public int MyProperty
  4.     {
  5.         // 只需使用 field
  6.         get => field;
  7.         set
  8.         {
  9.             if (field != value)
  10.             {
  11.                 field = value;
  12.                 OnPropertyChanged();
  13.             }
  14.         }
  15.     }
  16. }
复制代码

不再需要自己界说字段,只需使用 field 关键字,字段就会主动天生。

这也非常方便!

部分属性


在编写 C# 时,常见的标题之一是:属性不能添加 partial 修饰符。

在 C# 中,可以在类或方法上添加 partial,以便分别进行声明和实现。别的,还可以分散类的各个部分。它的紧张用途是在使用源代码天生器等主动天生工具时,指定要天生的内容。

比方:
  1. partial class ViewModel
  2. {
  3.     // 这里只声明方法,实现部分由工具自动生成
  4.     partial void OnPropertyChanged(string propertyName);
  5. }
复制代码

然后主动天生工具会天生以下代码:
  1. partial class ViewModel : INotifyPropertyChanged
  2. {
  3.     public event PropertyChangedEventHandler? PropertyChanged;
  4.     partial void OnPropertyChanged(string propertyName)
  5.     {
  6.         PropertyChanged?.Invoke(this, new(propertyName));
  7.     }
  8. }
复制代码

开辟者只需要声明 OnPropertyChanged,其实现将全部由主动天生,从而节流了开辟者的时间。

从 C# 13 开始,属性也支持 partial:
  1. partial class ViewModel
  2. {
  3.     // 声明部分属性
  4.     public partial int MyProperty { get; set; }
  5. }
  6. partial class ViewModel
  7. {
  8.     // 部分属性的实现
  9.     public partial int MyProperty
  10.     {
  11.         get
  12.         {
  13.             // ...
  14.         }
  15.         set
  16.         {
  17.             // ...
  18.         }
  19.     }
  20. }
复制代码

如许,属性也可以由工具主动天生了。

锁对象


众所周知,lock 是一种功能,通过监督器用于线程同步。
  1. object lockObject = new object();
  2. lock (lockObject)
  3. {
  4.     // 关键区
  5. }
复制代码

但是,这个功能的开销其实很大,会影响性能。

为了解决这个标题,C# 13 实现了锁对象。要使用此功能,只需用 System.Threading.Lock 替换被锁定的对象即可:
  1. using System.Threading;
  2. Lock lockObject = new Lock();
  3. lock (lockObject)
  4. {
  5.     // 关键区
  6. }
复制代码

如许就可以轻松提高性能了。

初始化器中的尾部索引


索引运算符 ^ 可用于表示集合末端的相对位置。从 C# 13 开始,初始化器也支持此功能:
  1. var x = new Numbers
  2. {
  3.     Values =
  4.     {
  5.         [1] = 111,
  6.         [^1] = 999 // ^1 是从末尾开始的第一个元素
  7.     }
  8.     // x.Values[1] 是 111
  9.     // x.Values[9] 是 999,因为 Values[9] 是最后一个元素
  10. };
  11. class Numbers
  12. {
  13.     public int[] Values { get; set; } = new int[10];
  14. }
复制代码

ESCAPE 字符


在 Unicode 字符串中,可以使用 \e 代替 \u001b 和 \x1b。\u001b、\x1b 和 \e 都表示 ESCAPE 字符。它们通常用于表示控制字符。



  • \u001b 表示 Unicode 转义序列,\u 后面的 4 位十六进制数表示 Unicode 代码点
  • \x1b 表示十六进制转义序列,\x 后面的 2 位十六进制数表示 ASCII 代码
  • \e 表示 ESCAPE 字符自己

保举使用 \e 的原因是,可以制止在十六进制中的肴杂。

比方,假如 \x1b 后面跟着 3,则变为 \x1b3,由于 \x1b 和 3 之间没有明确的分隔,因此不清晰应该分别表明成 \x1b 和 3,照旧放在一起表明。

假如使用 \e,则可以制止肴杂。

其他


除了上述功能外,方法组中的自然范例和方法重载中的优先级也有一些改进,但在本文中省略。假如想了解更多信息,请参阅文档。

结语


C# 正在年复一年地进化,对我来说 C# 13 的更新中实现了许多非常实用且方便的功能,解决了不少实际的痛点。等待 .NET 9 和 C# 13 的正式发布~

   文章转载自:hez2010
  原文链接:https://www.cnblogs.com/hez2010/p/18326521/whats-new-in-csharp-13
  体验地址:引迈 - JNPF快速开辟平台_低代码开辟平台_零代码开辟平台_流程计划器_表单引擎_工作流引擎_软件架构

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表