您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。
摘要:前言 C# 11 中即将到来一个可以让重视性能的开发者狂喜的重量级特性,这个特性主要是围绕着一个重要底层性能设施 ref 和 struct 的一系列改进。 但是这部分的改进涉及的内容较多,不一定能在 .NET 7(C# 11)做完,因此部分
前言
C# 11 中即将到来一个可以让重视性能的开发者狂喜的重量级特性,这个特性主要是围绕着一个重要底层性能设施 ref 和 struct 的一系列改进。
但是这部分的改进涉及的内容较多,不一定能在 .NET 7(C# 11)做完,因此部分内容推迟到 C# 12 也是有可能的。当然,还是很有希望能在 C# 11 的时间点就看到完全体的。
本文仅仅就这一个特性进行介绍,因为 C# 11 除了本特性之外,还有很多其他的改进,一篇文章根本说不完,其他那些我们就等到 .NET 7 快正式发布的时候再说吧。
背景
C# 自 7.0 版本引入了新的 ref struct 用来表示不可被装箱的栈上对象,但是当时局限性很大,甚至无法被用于泛型约束,也无法作为 struct 的字段。在 C# 11 中,由于特性 ref 字段的推动,需要允许类型持有其它值类型的引用,这方面的东西终于有了大幅度进展。
这些设施旨在允许开发者使用安全的代码编写高性能代码,而无需面对不安全的指针。接下来我就来对 C# 11 甚至 12 在此方面的即将到来的改进进行介绍。
ref 字段
C# 以前是不能在类型中持有对其它值类型的引用的,但是在 C# 11 中,这将变得可能。从 C# 11 开始,将允许 ref struct 定义 ref 字段。
readonly ref struct Span<T>
{
private readonly ref T _field;
private readonly int _length;
public Span(ref T value)
{
_field = ref value;
_length = 1;
}
}
直观来看,这样的特性将允许我们写出上面的代码,这段代码中构造了一个 Span<T>,它持有了对其他 T 对象的引用。
当然,ref struct 也是可以被 default 来初始化的:
Span<int> span = default;
但这样 _field 就会是个空引用,不过我们可以通过 Unsafe.IsNullRef 方法来进行检查:
if (Unsafe.IsNullRef(ref _field))
{
throw new NullReferenceException(...);
}
另外,ref字段的可修改性也是一个非常重要的事情,因此引入了:
readonly ref:一个对对象的只读引用,这个引用本身不能在构造方法或 init 方法之外被修改
ref readonly:一个对只读对象的引用,这个引用指向的对象不能在构造方法或 init 方法之外被修改
readonly ref readonly:一个对只读对象的只读引用,是上述两种的组合
例如:
ref struct Foo
{
ref readonly int f1;
readonly ref int f2;
readonly ref readonly int f3;
void Bar(int[] array)
{
f1 = ref array[0]; // 没问题
f1 = array[0]; // 错误,因为 f1 引用的值不能被修改
f2 = ref array[0]; // 错误,因为 f2 本身不能被修改
f2 = array[0]; // 没问题
f3 = ref array[0]; // 错误:因为 f3 本身不能被修改
f3 = array[0]; // 错误:因为 f3 引用的值不能被修改
}
}
生命周期
这一切看上去都很美好,但是真的没有任何问题吗?
假设我们有下面的代码来使用上面的东西:
Span<int> Foo()
{
int v = 42;
return new Span<int>(ref v);
}
v 是一个局部变量,在函数返回之后其生命周期就会结束,那么上面这段代码就会导致 Span<int> 持有的 v 的引用变成无效的。顺带一提,上面这段代码是完全合法的,因为 C# 之前不支持 ref 字段,因此上面的代码是不可能出现逃逸问题的。但是 C# 11 加入了 ref 字段,栈上的对象就有可能通过 ref 字段而发生引用逃逸,于是代码变得不安全。
