[译]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() = (
        01 00 00 00
    )
.custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = (
        01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d
        62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65
        73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72
        74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73
        69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70
        69 6c 65 72 2e 01 00 00
    )
.custom instance void [System.Runtime]System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
        01 00 00 00
    )
// 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 8
// (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 8
// 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) = (
            01 00 01 00 00 00 00 00
        )
// Method begins at RVA 0x20a8
// Code size 6 (0x6)
.maxstack 8
// 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) = (
            01 00 01 00 00 00 00 00
        )
// Method begins at RVA 0x20af
// Code size 6 (0x6)
.maxstack 8
// 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 = 0;
        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或更高版本。

系列文章:

原文地址:https://www.cnblogs.com/wenhx/p/csharp-7-series-part-9-ref-structs.html

时间: 2024-11-08 22:40:38

[译]C# 7系列,Part 9: ref structs ref结构的相关文章

[译]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++,有一个const参数或类似的概念:这表明方法体中的参数是一个不能被重新赋值的常量.它有助于避免在方法体内无意中重新赋值一个方法参数的错误,并通过不允许

从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现

看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/...) 从 0 到 1 实现 React 系列 -- JSX 和 Virtual DOM 从 0 到 1 实现 React 系列 -- 组件和 state|props 从 0 到 1 实现 React 系列 -- 生命周期和 diff 算法 从 0 到 1 实现 React 系列 -- 优化 set

[译]C# 7系列,Part 2: Async Main

原文:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/ 你大概知道,C#语言可以构建两种程序.一种是带有入口点(entrypoint)的程序,这样操作系统就可以加载程序并从入口点执行;另一个是没有入口点的程序.操作系统不能直接执行程序,程序可以被其他有入口点的程序引用,这样就可以执行其中的代码. 必须有入口点的应用程序类型有:Windows Forms应用程序.UWP应用程序.控制台应

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

原文:https://blogs.msdn.microsoft.com/mazhou/2017/10/05/c-7-series-part-5-private-protected/ C#有几个可访问性修饰符,public.internal.internal protected和private. public: 具有此可访问性声明的成员可以在此成员所在程序集中或引用该成员所在程序集的任何其他程序集中可见.也就是说,访问不受限制. internal:具有此可访问性声明的成员可以在此成员所在程序集中可

Caffe学习系列——工具篇:神经网络模型结构可视化

Caffe学习系列--工具篇:神经网络模型结构可视化 在Caffe中,目前有两种可视化prototxt格式网络结构的方法: 使用Netscope在线可视化 使用Caffe提供的draw_net.py 本文将就这两种方法加以介绍 1. Netscope:支持Caffe的神经网络结构在线可视化工具 Netscope是个支持prototxt格式描述的神经网络结构的在线可视工具,网址:  http://ethereon.github.io/netscope/quickstart.html  它可以用来可

C#程序员学习Android开发系列之Android项目的目录结构

今天开始正式学习Android开发的种种细节,首先从最基本的概念和操作学起. 首先看一下Android项目的目录结构. 这是我随便建立的一个test项目,我们重点关注一下几个方面的内容: 1.src目录:存放java源代码的目录,里面建立一个包,包里面有4个java源文件(分别都继承自Activity).由于java要求比较严格,因此要求类名与文件名一致. gen(Generated Java Files)目录:自动产生Java源文件的目录,是由工具自动生成的,一般不需要自己修改.里面主要有一个

Go 系列教程 —— 第 16 部分:结构体

什么是结构体? 结构体是用户定义的类型,表示若干个字段(Field)的集合.有时应该把数据整合在一起,而不是让这些数据没有联系.这种情况下可以使用结构体. 例如,一个职员有 firstName.lastName 和 age 三个属性,而把这些属性组合在一个结构体 employee 中就很合理. 结构体的声明 type Employee struct { firstName string lastName string age int } 在上面的代码片段里,声明了一个结构体类型 Employee

[译]GC专家系列2:Java 垃圾回收的监控

原文链接:http://www.cubrid.org/blog/dev-platform/how-to-monitor-java-garbage-collection/ 这是"成为GC专家系列"文章的第二篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.到目前为止,你应该已经了解了JDK 7中的5种GC类型,以及每种GC对性能的影响. 在本篇中,我将介绍JVM在真实环境中如何运行GC的. 什么是GC监控 GC监控 指的是在运