[译]C# 7系列,Part 9: ref structs ref结构
原文:https://blogs.msdn.microsoft.com/mazhou/2018/03/02/c-7-series-part-9-ref-structs/
背景
在之前的文章中,我解释了许多新的C#特性,每一个特性都是为了增强语言或者解决问题而引入的。具体来说,我解释了值类型和引用类型、按值传递参数、按引用传递参数、ref局部变量和ref返回结果以及in参数。这其中许多功能是为高性能场景设计的。
ref和in参数可以帮助避免复制值,从而减少内存分配。当你有分配在堆栈的局部变量作为方法的实际参数传递时,这么做是有效率的的,在这种情况下,所有的分配都在堆栈上;不需要堆分配。
对于高性能和原生开发场景,你可能希望“仅限堆栈”类型始终停留在执行堆栈上,因此对这种类型的对象的操作只能发生在堆栈上,在作用域中公开给托管堆的任何对这种类型的外部引用都应该被禁止。
ref结构
ref struct是仅在堆栈上的值类型:
- 表现一个顺序结构的布局;(译注:可以理解为连续内存)
- 只能在堆栈上使用。即用作方法参数和局部变量;
- 不能是类或正常结构的静态或实例成员;
- 不能是异步方法或lambda表达式的方法参数;
- 不能动态绑定、装箱、拆箱、包装或转换。
ref struct也被称为嵌入式引用。
示例
下面的代码定义了一个ref结构。
public ref struct MyRefStruct
{
public int MyIntValue1;
public int MyIntValue2; [EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => throw new NotSupportedException(); [EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => throw new NotSupportedException(); [EditorBrowsable(EditorBrowsableState.Never)]
public override string ToString() => throw new NotSupportedException();
}
请注意,我已经覆盖了从System.Object继承的Equals、GetHashCode和ToString方法。因为ref结构不允许装箱,所以你将无法调用这两个(译注:原文两个,应该是三个)基方法。
你可以在方法参数或局部变量中使用MyRefStruct作为常规值类型,但不能在其他地方使用它。


你也可以创建只读ref结构,只需将readonly指令添加到ref结构声明中即可。
public readonly ref struct MyRefStruct
{
public readonly int MyIntValue1;
public readonly int MyIntValue2; public MyRefStruct(int value1, int value2)
{
this.MyIntValue1 = value1;
this.MyIntValue2 = value2;
} [EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) => throw new NotSupportedException(); [EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => throw new NotSupportedException(); [EditorBrowsable(EditorBrowsableState.Never)]
public override string ToString() => throw new NotSupportedException();
}
与常规只读结构一样,需要将所有实例字段/属性设置为只读。
元数据
ref结构在C# 7.2中可用。此功能需要编译器级别更改才能工作,以便与以前编译器生成的程序集向后兼容。编译器会为ref结构声明发出[Obsolete]和[IsByRefLike]特性。
如果任何旧的程序集引用了包含ref结构类型的库,[Obsolete]属性将影响并阻止代码编译。
下面是为上面的ref结构声明生成的IL。
.class public sequential ansi sealed beforefieldinit Demo.MyRefStruct
extends [System.Runtime]System.ValueType
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( )
.custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = (
6d
6e
6e 6f 6f
6e
6f 6e 6f 6f 6f 6d
6c 2e
)
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( )
// Fields
.field public initonly int32 MyIntValue1
.field public initonly int32 MyIntValue2
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
int32 value1,
int32 value2
) cil managed
{
// Method begins at RVA 0x2090
// Code size 16 (0x10)
.maxstack
// (no C# code)
IL_0000: nop
// this.MyIntValue1 = value1;
IL_0001: ldarg.0
IL_0002: ldarg.1
IL_0003: stfld int32 Demo.MyRefStruct::MyIntValue1
// this.MyIntValue2 = value2;
IL_0008: ldarg.0
IL_0009: ldarg.2
IL_000a: stfld int32 Demo.MyRefStruct::MyIntValue2
// (no C# code)
IL_000f: ret
} // end of method MyRefStruct::.ctor
.method public hidebysig virtual
instance bool Equals (
object obj
) cil managed
{
// Method begins at RVA 0x20a1
// Code size 6 (0x6)
.maxstack
// throw new NotSupportedException();
IL_0000: newobj instance void [System.Runtime]System.NotSupportedException::.ctor()
// (no C# code)
IL_0005: throw
} // end of method MyRefStruct::Equals
.method public hidebysig virtual
instance int32 GetHashCode () cil managed
{
.custom instance void [System.Runtime]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System.Runtime]System.ComponentModel.EditorBrowsableState) = ( )
// Method begins at RVA 0x20a8
// Code size 6 (0x6)
.maxstack
// throw new NotSupportedException();
IL_0000: newobj instance void [System.Runtime]System.NotSupportedException::.ctor()
// (no C# code)
IL_0005: throw
} // end of method MyRefStruct::GetHashCode
.method public hidebysig virtual
instance string ToString () cil managed
{
.custom instance void [System.Runtime]System.ComponentModel.EditorBrowsableAttribute::.ctor(valuetype [System.Runtime]System.ComponentModel.EditorBrowsableState) = ( )
// Method begins at RVA 0x20af
// Code size 6 (0x6)
.maxstack
// throw new NotSupportedException();
IL_0000: newobj instance void [System.Runtime]System.NotSupportedException::.ctor()
// (no C# code)
IL_0005: throw
} // end of method MyRefStruct::ToString
} // end of class Demo.MyRefStruct
Span<T>和Memory<T>
有了类ref类型的支持,现在就可以为所有连续内存访问提供统一的类型。System.Span<T>表示内存的连续空间,可用于执行堆栈、托管堆和非托管堆的通用内存操作。
下面是ReadOnlySpan<T>的一个简单用法,用于去掉字符串的开始的空格。
internal class Program
{
private static void Main(string[] args)
{
string text = " I am using C# 7.2 Span<T>!";
Console.WriteLine(TrimStart(text).ToArray());
} private static ReadOnlySpan<char> TrimStart(ReadOnlySpan<char> text)
{
if (text.IsEmpty)
{
return text;
} int i = ;
char c; while ((c = text[i]) == ' ')
{
i++;
} return text.Slice(i);
}
}
结论
C# 7.2为高性能场景添加了语言特性,并为低级别的原生开发和互操作性场景提供了效率。ref结构还可以与stackalloc、Span<T>、fixed buffers和Ranges(C# 7.3)一起用于生产力。
注意:要使用这个特性,需要Visual Studio 2017 15.5或更高版本。
系列文章:
- [译]C# 7系列,Part 1: Value Tuples 值元组
- [译]C# 7系列,Part 2: Async Main 异步Main方法
- [译]C# 7系列,Part 3: Default Literals 默认文本表达式
- [译]C# 7系列,Part 4: Discards 弃元
- [译]C# 7系列,Part 5: private protected 访问修饰符
- [译]C# 7系列,Part 6: Read-only structs 只读结构
- [译]C# 7系列,Part 7: ref Returns ref返回结果
- [译]C# 7系列,Part 8: in Parameters in参数
- [译]C# 7系列,Part 9: ref structs ref结构 (本文)
- [译]C# 7系列,Part 10: Span<T> and universal memory management Span<T>和统一内存管理 (完)
[译]C# 7系列,Part 9: ref structs ref结构的更多相关文章
- [译]C# 7系列,Part 1: Value Tuples 值元组
Mark Zhou写了很不错的一系列介绍C# 7的文章,虽然是2年多年前发布的,不过对于不熟悉C# 7特性的同学来说,仍然有很高的阅读价值. 原文:https://blogs.msdn.microso ...
- [译]C# 7系列,Part 6: Read-only structs 只读结构
原文:https://blogs.msdn.microsoft.com/mazhou/2017/11/21/c-7-series-part-6-read-only-structs/ 背景 在.NET世 ...
- [译]C# 7系列,Part 7: ref Returns ref返回结果
原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/ 背景 有两种方法可以将一个值传 ...
- [译]C# 7系列,Part 8: in Parameters in参数
原文:https://blogs.msdn.microsoft.com/mazhou/2018/01/08/c-7-series-part-8-in-parameters/ 背景 默认情况下,方法参数 ...
- [译]C# 7系列,Part 2: Async Main 异步Main方法
原文:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/ 你大概知道,C#语言可以构建两种 ...
- [译]C# 7系列,Part 3: Default Literals 默认文本表达式
原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/06/c-7-series-part-3-default-literals/ C#的default ...
- [译]C# 7系列,Part 4: Discards 弃元
原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/27/c-7-series-part-4-discards/ 有时我们想要忽略一个方法返回的值,特 ...
- [译]C# 7系列,Part 5: private protected 访问修饰符
原文:https://blogs.msdn.microsoft.com/mazhou/2017/10/05/c-7-series-part-5-private-protected/ C#有几个可访问性 ...
- [译]C# 7系列,Part 10: Span<T> and universal memory management Span<T>和统一内存管理
原文:https://blogs.msdn.microsoft.com/mazhou/2018/03/25/c-7-series-part-10-spant-and-universal-memory- ...
随机推荐
- drf组件之jwt认证
drf组件之jwt认证模块 一.认证规则 全称:json web token 解释:加密字符串的原始数据是json,后台产生,通过web传输给前台存储 格式:三段式 - 头.载荷.签名 - 头和载荷才 ...
- Redis面试题详解:哨兵+复制+事务+集群+持久化等
Redis主要有哪些功能? 1.哨兵(Sentinel)和复制(Replication) Redis服务器毫无征兆的罢工是个麻烦事,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制. ...
- JavaScript笔记九
1.数组方法 reverse() - 可以用来反转一个数组,它会对原数组产生影响 concat() - 可以连接两个或多个数组,它不会影响原数组,而是新数组作为返回值返回 join() - 可以将一个 ...
- 并发编程--greenlet与gevent
什么是greenlet? 虽然CPython(标准Python)能够通过生成器来实现协程,但使用起来还并不是很方便. 与此同时,Python的一个衍生版 Stackless Python实现了原生的协 ...
- ip地址计算
1.多少个子网? 2x个,其中x为被遮盖(取值为1)的位数.例如,在11000000(这个值是子网掩码的最后几位,例如,mask=18)中,取值为1的位数为2,因此子网数位22=4个: 2.每个子网包 ...
- 纵论WebAssembly,JS在性能逆境下召唤强援
webassembly的作用 webassembly是一种底层的二进制数据格式和一套可以操作这种数据的JS接口的统称.我们可以认为webassembly的范畴里包含两部分 wasm: 一种体积小.加载 ...
- 教你如何提高 PHP 代码的质量
说实话,在代码质量方面,PHP 的压力非常大.通过阅读本系列文章,您将了解如何提高 PHP 代码的质量. 我们可以将此归咎于许多原因,但这肯定不仅仅是因为 PHP 生态系统缺乏适当的测试工具.在本文中 ...
- synchronized被这么问,谁能受得了
synchronized是面试中经常会被问到的知识点,相关的问题点也很多,问题答案涉及的知识点也很多,有经验的面试官就会顺着你的答案不断追问一下,下面的对话场景就是相关面试题的连环炮. 面试官:说一下 ...
- Thinkphp5——数据库表名的大小写问题
ThinkPHP5中数据库的表名如果是驼峰命名法,会被转换成小写加下划线,解决方法如下: 1.表名全部小写,因为数据库的表名区分大小写的. 2.使用Db::table("表名"), ...
- java.lang.ClassNotFoundException: com.demo.search.extractAbstract.service.ExtractAbstractServiceHandler
在利用 Spring 对 thrift 进行集成时,出现错误: avax.servlet.ServletException: Servlet.init() for servlet search-nlp ...