原文: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 9: ref structs ref结构的更多相关文章

  1. [译]C# 7系列,Part 1: Value Tuples 值元组

    Mark Zhou写了很不错的一系列介绍C# 7的文章,虽然是2年多年前发布的,不过对于不熟悉C# 7特性的同学来说,仍然有很高的阅读价值. 原文:https://blogs.msdn.microso ...

  2. [译]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世 ...

  3. [译]C# 7系列,Part 7: ref Returns ref返回结果

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/12/12/c-7-series-part-7-ref-returns/ 背景 有两种方法可以将一个值传 ...

  4. [译]C# 7系列,Part 8: in Parameters in参数

    原文:https://blogs.msdn.microsoft.com/mazhou/2018/01/08/c-7-series-part-8-in-parameters/ 背景 默认情况下,方法参数 ...

  5. [译]C# 7系列,Part 2: Async Main 异步Main方法

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/ 你大概知道,C#语言可以构建两种 ...

  6. [译]C# 7系列,Part 3: Default Literals 默认文本表达式

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/06/c-7-series-part-3-default-literals/ C#的default ...

  7. [译]C# 7系列,Part 4: Discards 弃元

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/27/c-7-series-part-4-discards/ 有时我们想要忽略一个方法返回的值,特 ...

  8. [译]C# 7系列,Part 5: private protected 访问修饰符

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/10/05/c-7-series-part-5-private-protected/ C#有几个可访问性 ...

  9. [译]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- ...

随机推荐

  1. Python 操作Gitlab-API 实现批量的合并分支

    1.需求:每次大批量上线完成后,都会进行将hotfix合并到Master,合并到test/uat等等重复操作(上线发布后自动合并master已完成). 2.现实:在完成发布后自动合并master后,可 ...

  2. Java 添加、读取、修改、删除Word文档属性

    Word文档属性包括常规.摘要.统计.内容.自定义等,其中摘要包括标题.主题.作者.经理.单位.类别.关键词.备注等项目,通过设置这些摘要信息或自定义属性可方便对文档的管理.本文中将主要介绍对文档摘要 ...

  3. Handler+Looper+MessageQueue深入详解

    概述:Android中的异步处理机制由四部分组成:Handler+Looper+MessageQueue+message,用于实现线程间的通信. 用到的概念: Handler: 主要作用是发送消息和处 ...

  4. 图解 Spring:HTTP 请求的处理流程与机制【3】

    3. HTTP 请求在 Web 应用中的处理流程 在穿越了 Web 容器之后,HTTP 请求将被投送到 Web 应用,我们继续以 Tomcat 为例剖析后续流程.Web 容器与 Web 应用的衔接是通 ...

  5. Dubbo面试八连问,这些你都能答上来吗?

    Dubbo是什么? Dubbo能做什么? Dubbo内置了哪几种服务容器? Dubbo 核心的配置有哪些? Dubbo有哪几种集群容错方案,默认是哪种? Dubbo有哪几种负载均衡策略,默认是哪种? ...

  6. https揭秘

    首先简要说明一下所谓的https证书是什么东西:打个比方,你第一次去银行办理业务的时候都需要手持本人身份中去办理业务,这个身份证从哪里来呢,没错,是从国家相关机关得来的,在中国内是通用的,类比到htt ...

  7. 《软件安装》centos 安装 mysql

    上期问题回顾 全球 IPv4 地址正式耗尽,IPv4地址大约42.9亿,按照理论来说,每一个联网的设备都需要IP地址,而现在全球联网设备远远不止42.9亿,那么,这么多设备是怎么处理联网的问题呢? 先 ...

  8. 痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU启动那些事(2)- Boot配置(ISP_Pin, OTP)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MX RTxxx系列MCU的Boot配置. 在上一篇文章 Boot简介 里痞子衡为大家介绍了Boot基本原理以及i.MXRTx ...

  9. P1055 ISBN号码

    题目描述 每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括99位数字.11位识别码和33位分隔符,其规定格式如x-xxx-xxxxx-x,其中符号-就是分隔符(键盘上的减号),最后一位 ...

  10. 【Android - 自定义View】之自定义可下拉刷新或上拉加载的ListView

    首先来介绍一下这个自定义View: (1)这个自定义View的名称叫做 RefreshableListView ,继承自ListView类: (2)在这个自定义View中,用户可以设置是否支持下拉刷新 ...