原文: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.MyRefStructextends [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 rtspecialnameinstance 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 virtualinstance 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 virtualinstance 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 virtualinstance 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或更高版本,
系列文章:
- [譯]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>和統一記憶體管理 (完)
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/87766.html
標籤:C#
上一篇:C#泛型學習筆記
