少し前にGitHubで「reflikestruct」という用語を聞いたことがあると思います。
最新のC#バージョン(7.3)を手にしたので、ようやく自分でテストすることができました。したがって、これは有効なコードのようです。
public ref struct MyStruct
{
int x;
}
それについてのドキュメントがあるので、reflocalsとrefreturnsが何であるかを知っています。しかし、refstructに関するドキュメントが見つかりませんでした。
参照構造体は、自動プロパティまたはフィールドでは使用できません。オブジェクトにキャストすることもできません。これらは経験的な発見でした。
新しいc#が最近私に与えた「スパン」の背景から、ref構造体はスタックのみの構造体であると推測しました。それは決してヒープに進まない構造体です。しかし、100%確実ではありません。
これに関するドキュメントがあるはずですが、見つかりませんでした。
いくつかの調査の後、私は C#7.2のrefのようなタイプの安全性のコンパイル時の強制 に関するこの記事に出くわしました。
このC#機能は、「内部ポインター」または「参照のようなタイプ」とも呼ばれます。提案は、コンパイラが
Span<T>
などの特定のタイプがスタックにのみ表示されることを要求できるようにすることです。
このサイトには、主にガベージコレクションとスタック割り当てに関して、そうすることの利点も記載されています。
Refのような型を使用すると、次のようないくつかの制限もあります。
これにより、パラメータ、ローカル変数、場合によっては戻り値に使用できるように制限されます。
@UnholySheepがコメントで指摘したように、 Microsoftの公式ドキュメント もあります。
C#7.2へのこの追加は、実際には feature であり、そのようにマークされた新しい機能を追加または有効にするという意味ではありません。値型自体、むしろ、開発者が特定の制限を宣言または公開できるようにします。これは他の場所でのその型の許容される使用を管理します。
[編集:github/dotnetサイトの span-safety を参照]
したがって、ref struct
の指定が構造体のエンドユーザーに与えるものを検討する代わりに、それが作成者にどのように役立つかを検討してください。外部使用に制限を追加すると、論理的にはref struct
が想定する関連保証が必要になるため、キーワードの効果は、ref struct
が特定の特定のものを必要とすることを実行できるようにするか "ライセンス" になります。保証します。
重要なのは、それが間接のメリットであるということです。これは、通常ref struct
によってライセンス供与されていると見なされる種類の操作は、基本的にキーワードの問題ではなく、実装および試行できる可能性があるためです。 、ref struct
マーキング(またはそうでない)に関係なく、どこでも賢明なコードで。
理論的な部分についてはこれだけです。実際には、付随するすべての制限を受け入れるという極端な点でさえ、追加の保証に本質的に依存する「wilyコード」ユースケースとは何ですか?基本的に、これはstruct
が管理された参照をそれ自体に公開するまたはそのフィールドの1つを実行する機能です。
通常、C#は、this
のインスタンスメソッドからリークするstruct
参照に対して強い制限を適用します。
error CS8170: Struct members cannot return 'this' or other instance members by reference
コンパイラーは、this
が値型からリークする可能性が事実上ないことを確認する必要があります。これは、構造体インスタンスが呼び出しの目的で一時的にボックス化されている可能性があるためです(一部の用途では非常に可能性が高い)。インスタンスメソッド。この場合、GetPinnableReference
(またはその内部)へのマネージポインターが取得される可能性のある永続的なstruct
インスタンスはありません。
近年のすべてのref
の機能強化により、C#は、this
のエスケープを検出して禁止するために、さらに長い時間を費やしています。たとえば、上記に加えて、次のようになります。
error CS8157: Cannot return 'x' by reference because it was initialized to a value that cannot be returned by reference
..および...などの関連エラー.error CS8374: Cannot ref-assign 'foo' to 'p' because 'foo' has a narrower escape scope than 'p'.
コンパイラがCS8157
をアサートする根本的な理由が複雑であるか、わかりにくい場合がありますが、コンパイラは保守的な「申し訳ないより安全である」アプローチに固執します。これにより、たとえば、追加の特別な知識がある場合に誤検知が発生することがあります。エスケープは最終的にスタック内に含まれます。
CS8157
が本当に保証されていない場合(つまり、コンパイラが推測できなかった情報を考慮して)、これらは前に触れた「おそらく成功した」wilyコードの例を表しており、一般に簡単な回避策はありません。特にref struct
経由ではありません。これは、複雑な誤検知が発生するのは、高レベルのref
-パスコードシナリオでのみ発生することが多く、そもそもref struct
に適用される極端な制限を採用できないためです。
代わりに、ref struct
は非常に単純な値型に使用されます。 this
参照が常に上位スタックフレームに固定されることを保証することで(したがって、重要なことに、GCヒープに溢れることはありません)、そのようなタイプは、マネージポインターを自分自身または彼らのインテリア。
ただし、ref struct
は、それが提供するリラクゼーションがどのように、なぜ、何に使用されるかについては不可知論者であると言ったことを思い出してください。私が特にほのめかしていたのは、残念ながら、ref struct
を使用してもCS8157
が消えることはないということです(これはバグだと思います。 ここ および ここ を参照してください)。
独自のthis
を返すことを適切に許可する必要があるref struct
コードは、コンパイラーによってそれを防ぐことができるため、内でコーディングするときに致命的なエラーを回避するには、かなり野蛮な手法を使用する必要があります。おそらく解放されたref struct
インスタンスメソッド。つまり、C#で記述された値型インスタンスメソッドは、致命的なエラーを正当にオーバーライドする必要がありますCS8170
/CS8157
can 'this'ポインタを不透明にするそれをラウンドトリップすることによってIntPtr
。これは読者の演習として残されていますが、これを行う1つの方法は、 System。Runtime。CompilerServices。Unsafe パッケージを使用することです。
他の答えに少し追加するだけです。基本的に、彼らはマネージドポインタをメンバーとして保持できるようにref構造体を作成しました。これは、ガベージコレクションができないことを意味し、ヒープに到達すると、GCがクラッシュします。それを使ってできることとできないことに対する奇妙な制限は、すべてこれに関係しています(ここのMicrosoftのドキュメントで概説されています):
C#7.2の参照セマンティクスに関するMicrosoftドキュメント
これらはすべて完全に魅力的ですが、いったいなぜこの機能を提供したのかを実際には説明していません。本当の理由は、マネージメモリとアンマネージメモリの両方を処理するAPIが共通のインターフェースを持つことを許可することでした(つまり、無限のオーバーロードの必要性を排除します)。
これはこのブログで詳細に説明されています: