パターン ベースの fixed ステートメントPattern-based fixed statement

まとめSummary

fixed ステートメントに型を含めることができるパターンを導入します。Introduce a pattern that would allow types to participate in fixed statements.

目的Motivation

この言語は、マネージデータをピン留めし、基になるバッファーへのネイティブポインターを取得するためのメカニズムを提供します。The language provides a mechanism for pinning managed data and obtain a native pointer to the underlying buffer.

fixed(byte* ptr = byteArray)
{
   // ptr is a native pointer to the first element of the array
   // byteArray is protected from being moved/collected by the GC for the duration of this block 
}

fixed に参加できる型のセットはハードコーディングされており、配列と System.Stringに制限されています。The set of types that can participate in fixed is hardcoded and limited to arrays and System.String. ImmutableArray<T>Span<T>Utf8String などの新しいプリミティブが導入された場合、"特別な" 型をハードコーディングすることはできません。Hardcoding "special" types does not scale when new primitives such as ImmutableArray<T>, Span<T>, Utf8String are introduced.

さらに、System.String の現在のソリューションは、非常に厳密な API に依存しています。In addition, the current solution for System.String relies on a fairly rigid API. API の構造は、System.String が、オブジェクトヘッダーからの固定オフセットで、UTF16 でエンコードされたデータを埋め込む連続したオブジェクトであることを意味します。The shape of the API implies that System.String is a contiguous object that embeds UTF16 encoded data at a fixed offset from the object header. このような方法は、基になるレイアウトを変更する必要があるいくつかの提案で問題が見つかりました。Such approach has been found problematic in several proposals that could require changes to the underlying layout. System.String オブジェクトを、アンマネージ相互運用の目的で内部表現から分離することで、より柔軟なものに切り替えることができます。It would be desirable to be able to switch to something more flexible that decouples System.String object from its internal representation for the purpose of unmanaged interop.

詳細なデザインDetailed design

パターンPattern

実行可能なパターンベースの "固定" のニーズは次のとおりです。A viable pattern-based “fixed” need to:

  • インスタンスをピン留めするためのマネージ参照を指定し、ポインターを初期化します (これは同じ参照であることが望ましい)Provide the managed references to pin the instance and to initialize the pointer (preferably this is the same reference)
  • アンマネージ要素の型 ("string" の "char" など) を明確に伝達します。Convey unambiguously the type of the unmanaged element (i.e. “char” for “string”)
  • 参照するものがない場合、"empty" の場合の動作を指定します。Prescribe the behavior in "empty" case when there is nothing to refer to.
  • fixedの外部で型を使用するのを困難にする設計上の決定に対して、API 作成者をプッシュしないでください。Should not push API authors toward design decisions that hurt the use of the type outside of fixed.

前述のように、特別に名前が付けられた ref 戻りメンバーを認識することで、上記の条件を満たすことができます。 ref [readonly] T GetPinnableReference()I think the above could be satisfied by recognizing a specially named ref-returning member: ref [readonly] T GetPinnableReference().

fixed ステートメントで使用するには、次の条件を満たす必要があります。In order to be used by the fixed statement the following conditions must be met:

  1. 型に対して指定されたメンバーは1つだけです。There is only one such member provided for a type.
  2. ref または ref readonlyによってを返します。Returns by ref or ref readonly. (readonly は、安全なコードで使用できる書き込み可能な API を追加せずに、変更できない型または readonly の型の作成者がパターンを実装できるようにすることが許可されています)(readonly is permitted so that authors of immutable/readonly types could implement the pattern without adding writeable API that could be used in safe code)
  3. T はアンマネージ型です。T is an unmanaged type. (T* がポインター型になるためです。(since T* becomes the pointer type. 制限は、"アンマネージ" の概念が展開されている場合には自然に拡張されます)The restriction will naturally expand if/when the notion of "unmanaged" is expanded)
  4. ピン留めするデータがない場合に、マネージ nullptr を返します。これは、空である可能性が最も低い方法である可能性があります。Returns managed nullptr when there is no data to pin – probably the cheapest way to convey emptiness. (文字列が null で終わるため、"" という文字列は ' \ 0 ' への参照を返すことに注意してください)(note that “” string returns a ref to '\0' since strings are null-terminated)

また #3 では、空のケースの結果が未定義または実装固有であることを許可できます。Alternatively for the #3 we can allow the result in empty cases be undefined or implementation-specific. ただし、これにより、API の危険性が高くなり、誤用や意図しない互換性の負担が発生しやすくなります。That, however, may make the API more dangerous and prone to abuse and unintended compatibility burdens.

翻訳Translation

fixed(byte* ptr = thing)
{ 
    // <BODY>
}

は次の擬似コードになります ( C#では表現できないものがあります)becomes the following pseudocode (not all expressible in C#)

byte* ptr;
// specially decorated "pinned" IL local slot, not visible to user code.
pinned ref byte _pinned;

try
{
    // NOTE: null check is omitted for value types 
    // NOTE: `thing` is evaluated only once (temporary is introduced if necessary) 
    if (thing != null)
    {
        // obtain and "pin" the reference
        _pinned = ref thing.GetPinnableReference();

        // unsafe cast in IL
        ptr = (byte*)_pinned;
    }
    else
    {
        ptr = default(byte*);
    }

    // <BODY> 
}
finally   // finally can be omitted when not observable
{
    // "unpin" the object
    _pinned = nullptr;
}

短所Drawbacks

  • GetPinnableReference は fixedでのみ使用することを意図していますが、セーフコードでは使用できません。実装では、この点を念頭に置く必要があります。GetPinnableReference is intended to be used only in fixed, but nothing prevents its use in safe code, so implementor must keep that in mind.

代替Alternatives

ユーザーは GetPinnableReference または類似したメンバーを導入し、Users can introduce GetPinnableReference or similar member and use it as

fixed(byte* ptr = thing.GetPinnableReference())
{ 
    // <BODY>
}

代替ソリューションが必要な場合は、System.String の解決策はありません。There is no solution for System.String if alternative solution is desired.

未解決の質問Unresolved questions

  • [] 動作が "empty" 状態になります。[ ] Behavior in "empty" state.nullptr または undefined を - しますか? - nullptr or undefined ?
  • [] 拡張メソッドを考慮する必要がありますか。[ ] Should the extension methods be considered ?
  • [] System.Stringでパターンが検出された場合、それを勝ちにしますか?[ ] If a pattern is detected on System.String, should it win over ?

会議のデザインDesign meetings

まだありません。None yet.