[譯]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或更高版本。

 

系列文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章