当前位置:网站首页>Improvement of ref and struct in C 11
Improvement of ref and struct in C 11
2022-04-23 00:37:00 【Dotnet cross platform】
Preface
C# 11 There's a heavyweight feature coming in that can make performance conscious developers ecstatic , This feature is mainly around an important underlying performance facility ref and struct A series of improvements to .
However, the improvement of this part involves many contents , Not necessarily in .NET 7(C# 11) finish , Therefore, part of the content is postponed to C# 12 It's also possible . Of course , It is still very promising to be in C# 11 We'll see all of them in time .
This article only introduces this feature , because C# 11 In addition to this feature , There are many other improvements , There is no end to an article , The rest we'll wait until .NET 7 Let's talk about it when it's officially released .
background
C# since 7.0 Version introduces a new ref struct Used to represent objects on the stack that cannot be boxed , But there were great limitations at that time , Can't even be used for generic constraints , Nor can it serve as struct Field of . stay C# 11 in , Due to characteristics ref Field push , You need to allow types to hold references to other value types , Things in this area have finally made substantial progress .
These facilities are designed to allow developers to write high-performance code using secure code , Without having to face unsafe pointers . Next I'm going to C# 11 even to the extent that 12 The upcoming improvements in this area are introduced .
ref Field
C# Previously, you couldn't hold references to other value types in types , But in C# 11 in , This will become possible . from C# 11 Start , Will allow ref struct Definition ref Field .
readonly ref struct Span<T>
{
private readonly ref T _field;
private readonly int _length;
public Span(ref T value)
{
_field = ref value;
_length = 1;
}
}
Intuitive to see , Such a feature will allow us to write the above code , This code constructs a Span<T>, It holds on to other T References to objects .
Of course ,ref struct It can also be default To initialize :
Span<int> span = default;
But such _field It will be an empty reference , But we can go through Unsafe.IsNullRef Method to check :
if (Unsafe.IsNullRef(ref _field))
{
throw new NullReferenceException(...);
}
in addition ,ref The modifiability of fields is also a very important thing , So it introduces :
readonly ref: A read-only reference to an object , The reference itself cannot be in a constructor orinitModified outside the methodref readonly: A reference to a read-only object , The object pointed to by this reference cannot be in the constructor or init Modified outside the methodreadonly ref readonly: A read-only reference to a read-only object , It's a combination of the above two
for example :
ref struct Foo
{
ref readonly int f1;
readonly ref int f2;
readonly ref readonly int f3;
void Bar(int[] array)
{
f1 = ref array[0]; // That's all right.
f1 = array[0]; // error , because f1 The referenced value cannot be modified
f2 = ref array[0]; // error , because f2 Itself cannot be modified
f2 = array[0]; // That's all right.
f3 = ref array[0]; // error : because f3 Itself cannot be modified
f3 = array[0]; // error : because f3 The referenced value cannot be modified
}
}
Life cycle
All this looks beautiful , But is there really no problem ?
Suppose we have the following code to use the above things :
Span<int> Foo()
{
int v = 42;
return new Span<int>(ref v);
}
v Is a local variable , After the function returns, its life cycle will end , Then the above code will lead to Span<int> Held v The reference to becomes invalid . By the way , The above code is completely legal , because C# Not previously supported ref Field , Therefore, the above code is unlikely to have an escape problem . however C# 11 Joined the ref Field , Objects on the stack may pass through ref Field and reference escape , So the code becomes unsafe .
If we have one CreateSpan Method to create a reference Span :
Span<int> CreateSpan(ref int v)
{
// ...
}
This leads to a series of previous C# No problem ( because ref The life cycle of is the current method ), But in C# 11 Because there may be ref Field, resulting in unsafe code written in a safe way :
Span<int> Foo(int v)
{
// 1
return CreateSpan(ref v);
// 2
int local = 42;
return CreateSpan(ref local);
// 3
Span<int> span = stackalloc int[42];
return CreateSpan(ref span[0]);
}
therefore , stay C# 11 You have to introduce disruptive changes , The above code is not allowed to compile . But that doesn't completely solve the problem .
In order to solve the problem of escape , C# 11 Formulated the reference escape safety rules . For a e In the field f:
If
fIt's arefField , alsoeyesthis, befIn the method it is surrounded, it refers to escape safetyOtherwise, if
fIt's arefField , befReference escape safety range andeThe same escape safety rangeOtherwise, if
eIs a reference type , befThe reference escape security scope of is the method that calls itotherwise
fReference escape safety range andeidentical
because C# Methods in can return references , So according to the above rules , One ref struct The method in will not be able to return a right or wrong ref References to fields :
ref struct Foo
{
private ref int _f1;
private int f2;
public ref int P1 => ref _f1; // That's all right.
public ref int P2 => ref _f2; // error , Because of the violation of Rule 4
}
In addition to citing escape safety rules , There is also the right ref Rules for assignment :
about
x.e1 = ref e2, amongxIt is safe to escape in the calling method , thate2Must be reference escape safe in the calling methodabout
e1 = ref e2, amonge1It's a local variable , thate2The reference escape safety range of must be at least the same ase1The reference escape safety range is as large as
therefore , According to the above rules , The following code is OK :
readonly ref struct Span<T>
{
readonly ref T _field;
readonly int _length;
public Span(ref T value)
{
// That's all right. , because x yes this,this Safe escape range and value The safe range of reference escape is to call methods , Meet the rules 1
_field = ref value;
_length = 1;
}
}
So naturally , You need to label the life cycle on fields and parameters , Help the compiler determine the escape range of the object .
And when we write code , You don't need to remember so many of the above rules , Because with the lifecycle annotation, everything becomes explicit and intuitive .
scoped
stay C# 11 in , Introduced scoped Keyword is used to limit the escape safety range :
| local variable s | Reference escape safety range | Escape safety range |
|---|---|---|
Span<int> s |
The current method | Calling method |
scoped Span<int> s |
The current method | The current method |
ref Span<int> s |
Calling method | Calling method |
scoped ref Span<int> s |
The current method | Calling method |
ref scoped Span<int> s |
The current method | The current method |
scoped ref scoped Span<int> s |
The current method | The current method |
among ,scoped ref scoped It's redundant , Because it can be ref scoped Implication . And we just need to know scoped The method used to escape to the current range , Is it very simple ?
In this way , We can escape the parameters ( Life cycle ) The annotation :
Span<int> CreateSpan(scoped ref int v)
{
// ...
}
then , The previous code has become no problem , Because it's all scoped ref:
Span<int> Foo(int v)
{
// 1
return CreateSpan(ref v);
// 2
int local = 42;
return CreateSpan(ref local);
// 3
Span<int> span = stackalloc int[42];
return CreateSpan(ref span[0]);
}
scoped It can also be used on local variables :
Span<int> Foo()
{
// error , because span Cannot escape the current method
scoped Span<int> span1 = default;
return span1;
// That's all right. , Because the escape safety range of the initializer is to call the method , because span2 You can escape to calling methods
Span<int> span2 = default;
return span2;
// span3 and span4 It's the same , Because the escape safety range of the initializer is the current method , Add do not add scoped It makes no difference
Span<int> span3 = stackalloc int[42];
scoped Span<int> span4 = stackalloc int[42];
}
in addition ,struct Of this Also added. scoped ref The escape range of , That is, the reference escape safety range is the current method , The escape safety range is to call the method .
The rest is and out、in Parameter matching , stay C# 11 in ,out The parameter will default to scoped ref, and in The parameter remains at the default of ref:
ref int Foo(out int r)
{
r = 42;
return ref r; // error , because r The reference escape security scope of is the current method
}
This is very useful , For example, the following common situation :
Span<byte> Read(Span<byte> buffer, out int read)
{
// ..
}
Span<int> Use()
{
var buffer = new byte[256];
// If not modified out Reference escape safety range , This will report an error , Because the compiler needs to consider read Can be regarded as ref Field returns
// If modified out Reference escape safety range , Then there will be no problem , Because the compiler doesn't need to consider read Can be regarded as ref Field returns
int read;
return Read(buffer, out read);
}
Here are some more examples :
Span<int> CreateWithoutCapture(scoped ref int value)
{
// error , because value The reference escape security scope of is the current method
return new Span<int>(ref value);
}
Span<int> CreateAndCapture(ref int value)
{
// That's all right. , because value The safe range of escape is limited to value Reference escape safety range , This scope is to call methods
return new Span<int>(ref value)
}
Span<int> ComplexScopedRefExample(scoped ref Span<int> span)
{
// That's all right. , because span The escape safety scope of is to call the method
return span;
// That's all right. , because refLocal The reference escape security scope of is the current method 、 The escape safe range is to call the method
// stay ComplexScopedRefExample In the call of, it is passed to a scoped ref Parameters ,
// It means that the compiler does not need to consider the reference escape safety range when calculating the life cycle , Just consider the escape safety range
// Therefore, the safe escape range of the value it returns is the calling method
Span<int> local = default;
ref Span<int> refLocal = ref local;
return ComplexScopedRefExample(ref refLocal);
// error , because stackLocal Reference escape safety range 、 Escape safety ranges are current methods
// stay ComplexScopedRefExample In the call of, it is passed to a scoped ref Parameters ,
// It means that the compiler does not need to consider the reference escape safety range when calculating the life cycle , Just consider the escape safety range
// Therefore, the safe escape range of the value it returns is the current method
Span<int> stackLocal = stackalloc int[42];
return ComplexScopedRefExample(ref stackLocal);
}
unscoped
In the above design , There is still a problem that has not been solved :
struct S
{
int _field;
// error , because this The reference escape security scope of is the current method
public ref int Prop => ref _field;
}
So introduce a unscoped, Allow the escape scope to be extended to the calling method , therefore , The above method can be rewritten as :
struct S
{
private int _field;
// That's all right. , The reference escape security scope is extended to call methods
public unscoped ref int Prop => ref _field;
}
This unscoped You can also put it directly into struct On :
unscoped struct S
{
private int _field;
public unscoped ref int Prop => ref _field;
}
Empathy , Nested struct No problem :
unscoped struct Child
{
int _value;
public ref int Value => ref _value;
}
unscoped struct Container
{
Child _child;
public ref int Value => ref _child.Value;
}
Besides , If you need to restore the previous out If the escape range , It can also be in out Parameter unscoped:
ref int Foo(unscoped out int r)
{
r = 42;
return ref r;
}
But about unscoped The design of is still in the preliminary stage , Not in C# 11 Is provided in .
ref struct constraint
from C# 11 Start ,ref struct It can be used as a generic constraint , So you can write the following method :
void Foo<T>(T v) where T : ref struct
{
// ...
}
therefore ,Span<T> The function of has also been extended , It can be stated that Span<Span<T>> 了 , For example, in byte perhaps char On , It can be used for high-performance string processing .
Reflection
With so many things on it , Reflection naturally needs to be supported . therefore , Reflection API And joined in ref struct Related support .
The actual cases
With the above infrastructure , We can use security code to build some high-performance wheels .
Fixed length list on stack
struct FrugalList<T>
{
private T _item0;
private T _item1;
private T _item2;
public readonly int Count = 3;
public unscoped ref T this[int index] => index switch
{
0 => ref _item1,
1 => ref _item2,
2 => ref _item3,
_ => throw new OutOfRangeException("Out of range.")
};
}
Stack linked list
ref struct StackLinkedListNode<T>
{
private T _value;
private ref StackLinkedListNode<T> _next;
public T Value => _value;
public bool HasNext => !Unsafe.IsNullRef(ref _next);
public ref StackLinkedListNode<T> Next => HasNext ? ref _next : throw new InvalidOperationException("No next node.");
public StackLinkedListNode(T value)
{
this = default;
_value = value;
}
public StackLinkedListNode(T value, ref StackLinkedListNode<T> next)
{
_value = value;
_next = ref next;
}
}
In addition to these two examples , Others, such as parsers and serializers , for example Utf8JsonReader、Utf8JsonWriter You can use these things .
Future plans
Advanced lifecycle
Although the above life cycle design can meet the needs of most users , But it's not flexible enough , Therefore, it is possible to expand on this basis in the future , Introduce advanced lifecycle annotations . for example :
void M(scoped<'a> ref MyStruct s, scoped<'b> Span<int> span) where 'b >= 'a
{
s.Span = span;
}
The above method gives parameters s and span Two life cycles are declared respectively 'a and 'b, And constraints 'b The life cycle of is not less than 'a, So in this method ,span Can be safely assigned to s.Span.
Although this will not be included in C# 11 in , However, if developers' demand for relevant products increases in the future , It is possible to be subsequently added to C# Medium .
summary
That's all C# 11( Or after ) Yes ref and struct Improved . With this infrastructure , Developers will be able to easily write high-performance code in a safe way without any heap memory overhead . Although these improvements can only directly benefit a small number of developers who are very concerned about performance , However, these improvements will lead to the overall improvement of the code quality and performance of the subsequent basic library .
If you're worried that this will increase the complexity of the language , It doesn't have to be , Because most people don't use these things , It will only affect a small number of developers . So for most people , Just write the original code , Enjoy other basic libraries, and the author can use the above facilities to write good things .
版权声明
本文为[Dotnet cross platform]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230032530775.html
边栏推荐
- L2-007 家庭房产 (25 分) 并查集或图的遍历
- 市场格局进入重构期,ESP频繁「召回」,中国供应商「乘势而上」
- 深度学习基础学习-残差
- js -对连续月份数据做处理拆分
- thymeleaf 的templates子集目录下不能引入common公共页面,否则报错:模板解析错误,错误原因:公共页面的引用路径错误(缺少子目录名)
- MySQL built-in function
- Mp2459 is a perfect replacement for 60v0 with power MOSFET fs2459 integrated inside 5A step-down IC
- 【图像分类】用最简短的代码复现SeNet,小白一定要收藏(keras,Tensorflow2.x)
- 33岁,工作十年被裁员,所谓经验根本不值钱
- 多测师杭州拱墅校区肖sir_高级金牌讲师_简历制作讲解
猜你喜欢

Mp2459 is a perfect replacement for 60v0 with power MOSFET fs2459 integrated inside 5A step-down IC

ArcMAP 使用绘图工具添加注记

Analytic hierarchy process software operation steps (Yaahp)

Static and dynamic control nixie tube

将 AWS S3 数据迁移至 TiDB Cloud 集群

js -对连续月份数据做处理拆分

Object size and pointer compression -- JVM

Intelligent wireless transmission module, cv5200 helps UAV mesh networking, wireless communication transmission scheme

在Google工作的十年,也是我开心的十年

(turn to) C # best tool set: IDE, analysis, automation tools, etc
随机推荐
js -对连续月份数据做处理拆分
L2-035 完全二叉树的层序遍历 (25 分)
[C#]给地球点颜色看看
洛谷P2241统计方形
L2-012 judgment on heap (25 points) (string bug to be solved)
396. Rotation function / Sword finger offer II 013 Sum of two-dimensional submatrix
ethtool查看网卡统计信息的流程
C# WPF UI框架MahApps切换主题
(turn) how is word2016 compatible with MathType
L2-007 家庭房产 (25 分) 并查集或图的遍历
ArcGIS urban living area land suitability evaluation (IV)
Object size and pointer compression -- JVM
[C] give the earth some color
2.58 - write the program is little endian, which returns 1 when compiled and run on the small end method machine and 0 when compiled and run on the large end method machine. This program should be abl
ArcGIS 城市生活区用地适宜性评价(四)
[image classification] - come on, let's do this bowl of efficientnet combat (pytoch)
L2-002 链表去重 (25 分) 标程
Pain points solved by tidb under the wave of localization
Acwing spring daily question - do you know ABC
Steps to apply for a CA certificate