読み取り専用の参照Readonly references

  • [x] が提案されています[x] Proposed
  • [x] プロトタイプ[x] Prototype
  • [x] 実装: 開始しました[x] Implementation: Started
  • [] 仕様: 開始されていません[ ] Specification: Not Started

まとめSummary

"読み取り専用の参照" 機能は実際には、参照によって変数を渡す効率性を活用し、変更するデータを公開しない機能のグループです。The "readonly references" feature is actually a group of features that leverage the efficiency of passing variables by reference, but without exposing the data to modifications:

  • in パラメータin parameters
  • ref readonly 戻り値ref readonly returns
  • readonly structreadonly structs
  • ref/in 拡張メソッドref/in extension methods
  • ref readonly [ローカル]ref readonly locals
  • ref 条件式ref conditional expressions

読み取り専用参照として引数を渡す。Passing arguments as readonly references.

このトピックには、 https://github.com/dotnet/roslyn/issues/115 特に多くの詳細を説明することなく、読み取り専用パラメーターの特殊なケースとして、既存の提案があります。There is an existing proposal that touches this topic https://github.com/dotnet/roslyn/issues/115 as a special case of readonly parameters without going into many details. ここでは、それ自体がまったく新しいものではないということを認識したいだけです。Here I just want to acknowledge that the idea by itself is not very new.

目的Motivation

この機能を使用する前に、C# では、変更を意図せずに、読み取り専用の目的で構造体変数をメソッド呼び出しに渡す必要があることを効率的に表現できませんでした。Prior to this feature C# did not have an efficient way of expressing a desire to pass struct variables into method calls for readonly purposes with no intention of modifying. 標準の値渡し引数は、不要なコストを追加するコピーを意味します。Regular by-value argument passing implies copying, which adds unnecessary costs. を使用すると、ユーザーは-ref 引数を渡し、コメント/ドキュメントに依存して、呼び出し先によるデータの変換が想定されていないことを示すことができます。That drives users to use by-ref argument passing and rely on comments/documentation to indicate that the data is not supposed to be mutated by the callee. 多くの理由により、これは適切な解決策ではありません。It is not a good solution for many reasons.
例としては、パフォーマンスに関する考慮事項のために、 XNA のように、単純に ref オペランドがあることがわかります。The examples are numerous - vector/matrix math operators in graphics libraries like XNA are known to have ref operands purely because of performance considerations. Roslyn コンパイラ自体には、割り当てを回避するために構造体を使用するコードがあり、その後、コストのコピーを回避するために参照によって渡されます。There is code in Roslyn compiler itself that uses structs to avoid allocations and then passes them by reference to avoid copying costs.

ソリューション ( in パラメーター)Solution (in parameters)

パラメーターと同様に outin パラメーターは、呼び出し先からの追加の保証を持つマネージ参照として渡されます。Similarly to the out parameters, in parameters are passed as managed references with additional guarantees from the callee.
out他の用途の前に呼び出し先によって割り当てられる 必要が あるパラメーターとは異なり、 in パラメーターを呼び出し先で割り当てることはできません。Unlike out parameters which must be assigned by the callee before any other use, in parameters cannot be assigned by the callee at all.

結果の in パラメーターを使用すると、呼び出し先によって変更に引数を公開せずに間接的な引数を渡すことができます。As a result in parameters allow for effectiveness of indirect argument passing without exposing arguments to mutations by the callee.

in パラメーターの宣言Declaring in parameters

in パラメーターは、パラメーターシグネチャの修飾子としてキーワードを使用して宣言され in ます。in parameters are declared by using in keyword as a modifier in the parameter signature.

すべての目的で、 in パラメーターは変数として扱われ readonly ます。For all purposes the in parameter is treated as a readonly variable. メソッド内でのパラメーターの使用に関する制限のほとんど in は、フィールドの場合と同じ readonly です。Most of the restrictions on the use of in parameters inside the method are the same as with readonly fields.

実際には、 in パラメーターはフィールドを表すことができ readonly ます。Indeed an in parameter may represent a readonly field. 制限の類似性は偶然ではありません。Similarity of restrictions is not a coincidence.

たとえば、 in 構造体型を持つパラメーターのフィールドは、すべて変数として再帰的に分類され readonly ます。For example fields of an in parameter which has a struct type are all recursively classified as readonly variables .

static Vector3 Add (in Vector3 v1, in Vector3 v2)
{
    // not OK!!
    v1 = default(Vector3);

    // not OK!!
    v1.X = 0;

    // not OK!!
    foo(ref v1.X);

    // OK
    return new Vector3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
  • in パラメーターは、通常の byval パラメーターが許可されている任意の場所で使用できます。in parameters are allowed anywhere where ordinary byval parameters are allowed. これには、インデクサー、演算子 (変換を含む)、デリゲート、ラムダ、ローカル関数が含まれます。This includes indexers, operators (including conversions), delegates, lambdas, local functions.
 (in int x) => x                                                     // lambda expression  
 TValue this[in TKey index];                                         // indexer
 public static Vector3 operator +(in Vector3 x, in Vector3 y) => ... // operator
  • in をと組み合わせて使用することはできません out 。また、と組み合わせることもできません outin is not allowed in combination with out or with anything that out does not combine with.

  • 相違に対してオーバーロードすることはできません ref / out / inIt is not permitted to overload on ref/out/in differences.

  • 通常の byval と相違点でオーバーロードすることができ in ます。It is permitted to overload on ordinary byval and in differences.

  • OHI の目的 (オーバーロード、非表示、実装) では、 in パラメーターと同じように動作し out ます。For the purpose of OHI (Overloading, Hiding, Implementing), in behaves similarly to an out parameter. 同じルールがすべて適用されます。All the same rules apply. たとえば、オーバーライドするメソッドでは、 in パラメーターを、 in id 変換可能な型のパラメーターと照合する必要があります。For example the overriding method will have to match in parameters with in parameters of an identity-convertible type.

  • デリゲート/ラムダ/メソッドグループ変換のために、は in パラメーターと同様に動作 out します。For the purpose of delegate/lambda/method group conversions, in behaves similarly to an out parameter. ラムダおよび適用可能なメソッドグループ変換の候補は、 in ターゲットデリゲートのパラメーターと、 in id 変換可能な型のパラメーターを一致させる必要があります。Lambdas and applicable method group conversion candidates will have to match in parameters of the target delegate with in parameters of an identity-convertible type.

  • 一般的な分散のために、 in パラメーターは非バリアントです。For the purpose of generic variance, in parameters are nonvariant.

注: in 参照型またはプリミティブ型を持つパラメーターには警告がありません。NOTE: There are no warnings on in parameters that have reference or primitives types. 一般には意味がないかもしれませんが、場合によっては、ユーザーがプリミティブをとして渡す必要があり in ます。It may be pointless in general, but in some cases user must/want to pass primitives as in. 例- Method(in T param) T がに置き換えられたとき int 、または次のようなメソッドがあるときなどのジェネリックメソッドをオーバーライドする Volatile.Read(in int location)Examples - overriding a generic method like Method(in T param) when T was substituted to be int, or when having methods like Volatile.Read(in int location)

パラメーターの使用効率が悪い場合に警告を表示するアナライザーがあると考えられ in ますが、そのような分析の規則は、言語仕様の一部としてあいまいになることはありません。It is conceivable to have an analyzer that warns in cases of inefficient use of in parameters, but the rules for such analysis would be too fuzzy to be a part of a language specification.

in 呼び出しサイトで使用します。Use of in at call sites. ( in 引数)(in arguments)

引数をパラメーターに渡すには、2つの方法があり in ます。There are two ways to pass arguments to in parameters.

in 引数は、次のパラメーターと一致することができ in ます。in arguments can match in parameters:

呼び出しサイトで修飾子を持つ引数は、 in パラメーターと一致させることができ in ます。An argument with an in modifier at the call site can match in parameters.

int x = 1;

void M1<T>(in T x)
{
  // . . .
}

var x = M1(in x);  // in argument to a method

class D
{
    public string this[in Guid index];
}

D dictionary = . . . ;
var y = dictionary[in Guid.Empty]; // in argument to an indexer
  • in 引数は 読み取り可能 な左辺値 (*) である必要があります。in argument must be a readable LValue(*). 例: M1(in 42) が無効ですExample: M1(in 42) is invalid

(*) 左辺値と右辺 値の概念は、言語によって異なります。(*) The notion of LValue/RValue vary between languages.
ここで、左辺値は、直接参照できる場所を表す式を意味します。Here, by LValue I mean an expression that represent a location that can be referred to directly. 右辺値は、それ自体には保持されない一時的な結果を生成する式を意味します。And RValue means an expression that yields a temporary result which does not persist on its own.

  • 特に readonly 、フィールド、 in パラメーター、またはその他の正式な readonly 変数を引数として渡すことができ in ます。In particular it is valid to pass readonly fields, in parameters or other formally readonly variables as in arguments. 例: dictionary[in Guid.Empty] は有効です。Example: dictionary[in Guid.Empty] is legal. Guid.Empty は、静的な読み取り専用フィールドです。Guid.Empty is a static readonly field.

  • in 引数は、パラメーターの型に 変換 可能な型である必要があります。in argument must have type identity-convertible to the type of the parameter. 例: M1<object>(in Guid.Empty) が無効です。Example: M1<object>(in Guid.Empty) is invalid. Guid.Empty はに 変換 できません。 objectGuid.Empty is not identity-convertible to object

上記の規則の目的は、引数が in 引数の変数の 別名 を保証することです。The motivation for the above rules is that in arguments guarantee aliasing of the argument variable. 呼び出し先は、常に、引数によって表される同じ場所への直接参照を受け取ります。The callee always receives a direct reference to the same location as represented by the argument.

  • まれに、 in 同じ呼び出しのオペランドとして使用される式によって引数がスタックに流出する必要がある場合は、 await との引数と同じ動作が使用され out ref ます。また、変数が、透過的な方法で書き込まれない場合は、エラーが報告されます。in rare situations when in arguments must be stack-spilled due to await expressions used as operands of the same call, the behavior is the same as with out and ref arguments - if the variable cannot be spilled in referentially-transparent manner, an error is reported.

例 :Examples:

  1. M1(in staticField, await SomethingAsync()) が有効です。M1(in staticField, await SomethingAsync()) is valid. staticField は、観測可能な副作用なしで複数回アクセスできる静的フィールドです。staticField is a static field which can be accessed more than once without observable side effects. したがって、副作用の順序とエイリアスの要件の両方を指定できます。Therefore both the order of side effects and aliasing requirements can be provided.
  2. M1(in RefReturningMethod(), await SomethingAsync()) によってエラーが生成されます。M1(in RefReturningMethod(), await SomethingAsync()) will produce an error. RefReturningMethod() は、を ref 返すメソッドです。RefReturningMethod() is a ref returning method. メソッド呼び出しには観察可能な副作用がある可能性があるため、オペランドの前に評価する必要があり SomethingAsync() ます。A method call may have observable side effects, therefore it must be evaluated before the SomethingAsync() operand. ただし、呼び出しの結果は、直接参照を必要としない中断ポイントで保持できない参照になり await ます。However the result of the invocation is a reference that cannot be preserved across the await suspension point which make the direct reference requirement impossible.

注: スタックの書き込むエラーは、実装固有の制限事項と見なされます。NOTE: the stack spilling errors are considered to be implementation-specific limitations. したがって、オーバーロードの解決やラムダの推論には影響しません。Therefore they do not have effect on overload resolution or lambda inference.

通常の byval 引数は、次のパラメーターと一致することができ in ます。Ordinary byval arguments can match in parameters:

修飾子のない通常の引数は、パラメーターと一致させることができ in ます。Regular arguments without modifiers can match in parameters. このような場合、引数の緩やかな制約は、通常の byval 引数と同じになります。In such case the arguments have the same relaxed constraints as an ordinary byval arguments would have.

このシナリオの目的は、 in api のパラメーターが、直接参照として渡すことができない場合 (例: リテラル、計算された結果、また await は複数の特定の型を持つ引数) に、ユーザーに対して防ぎが生じる可能性があることです。The motivation for this scenario is that in parameters in APIs may result in inconveniences for the user when arguments cannot be passed as a direct reference - ex: literals, computed or await-ed results or arguments that happen to have more specific types.
これらのすべてのケースには、引数の値を適切な型の一時的なローカルに格納し、そのローカルを引数として渡すという、簡単なソリューションがあり in ます。All these cases have a trivial solution of storing the argument value in a temporary local of appropriate type and passing that local as an in argument.
このような定型コードコンパイラの必要性を減らすために、必要に応じて、 in 呼び出しサイトに修飾子が存在しない場合は、同じ変換を実行できます。To reduce the need for such boilerplate code compiler can perform the same transformation, if needed, when in modifier is not present at the call site.

また、演算子の呼び出しや拡張メソッドなど、場合によっては、 in すべてを指定する構文的な方法はありません inIn addition, in some cases, such as invocation of operators, or in extension methods, there is no syntactical way to specify in at all. それだけでは、パラメーターに一致するときに通常の byval 引数の動作を指定する必要があり in ます。That alone requires specifying the behavior of ordinary byval arguments when they match in parameters.

特に次の点に違いがあります。In particular:

  • 右辺値を渡すことができます。it is valid to pass RValues. このような場合は、一時への参照が渡されます。A reference to a temporary is passed in such case. 例:Example:
Print("hello");      // not an error.

void Print<T>(in T x)
{
  //. . .
}
  • 暗黙的な変換は許可されます。implicit conversions are allowed.

これは、実際には右辺値を渡す特殊なケースです。This is actually a special case of passing an RValue

このような場合は、変換された一時的な値への参照が渡されます。A reference to a temporary holding converted value is passed in such case. 例:Example:

Print<int>(Short.MaxValue)     // not an error.
  • 拡張メソッドの受信者の場合 in ( ref 拡張メソッドではなく)、右辺値または暗黙的な 引数変換 が許可されます。in a case of a receiver of an in extension method (as opposed to ref extension methods), RValues or implicit this-argument-conversions are allowed. このような場合は、変換された一時的な値への参照が渡されます。A reference to a temporary holding converted value is passed in such case. 例:Example:
public static IEnumerable<T> Concat<T>(in this (IEnumerable<T>, IEnumerable<T>) arg)  => . . .;

("aa", "bb").Concat<char>()    // not an error.

拡張メソッドの詳細につい ref / in ては、こちらのドキュメントを参照してください。More information on ref/in extension methods is provided further in this document.

  • オペランドによる引数書き込むは await 、必要に応じて "値渡し" できます。argument spilling due to await operands could spill "by-value", if necessary. 引数への直接参照を指定するシナリオでは、引数の値のコピーが途中で書き込まれることが原因で、引数の値のコピーが書き込まれない場合があり await ます。In scenarios where providing a direct reference to the argument is not possible due to intervening await a copy of the argument's value is spilled instead.
    例:Example:
M1(RefReturningMethod(), await SomethingAsync())   // not an error.

副作用のある呼び出しの結果は、中断しても保持できない参照なの await で、実際の値を含む一時的な値は (通常の byval パラメーターの場合と同様に) 保持されます。Since the result of a side-effecting invocation is a reference that cannot be preserved across await suspension, a temporary containing the actual value will be preserved instead (as it would in an ordinary byval parameter case).

省略可能な引数を省略します。Omitted optional arguments

inパラメーターで既定値を指定できます。It is permitted for an in parameter to specify a default value. これにより、対応する引数が省略可能になります。That makes the corresponding argument optional.

呼び出しサイトで省略可能な引数を省略すると、一時によって既定値が渡されます。Omitting optional argument at the call site results in passing the default value via a temporary.

Print("hello");      // not an error, same as
Print("hello", c: Color.Black);

void Print(string s, in Color c = Color.Black)
{
    // . . .
}

一般的なエイリアス動作Aliasing behavior in general

変数 ref や変数と同じように outin 変数は既存の場所の参照/エイリアスです。Just like ref and out variables, in variables are references/aliases to existing locations.

呼び出し先は、それらへの書き込みを許可されていませんが、パラメーターを読み取る in と、他の評価の副作用として異なる値が観察される可能性があります。While callee is not allowed to write into them, reading an in parameter can observe different values as a side effect of other evaluations.

例:Example:

static Vector3 v = Vector3.UnitY;

static void Main()
{
    Test(v);
}

static void Test(in Vector3 v1)
{
    Debug.Assert(v1 == Vector3.UnitY);
    // changes v1 deterministically (no races required)
    ChangeV();
    Debug.Assert(v1 == Vector3.UnitX);
}

static void ChangeV()
{
    v = Vector3.UnitX;
}

in ローカル変数のパラメーターとキャプチャ。in parameters and capturing of local variables.

ラムダ/非同期キャプチャパラメーターの目的は、 in およびパラメーターと同じ out ref です。For the purpose of lambda/async capturing in parameters behave the same as out and ref parameters.

  • in クロージャでパラメーターをキャプチャすることはできませんin parameters cannot be captured in a closure
  • in 反復子メソッドではパラメーターは使用できませんin parameters are not allowed in iterator methods
  • in 非同期メソッドではパラメーターは使用できませんin parameters are not allowed in async methods

一時変数。Temporary variables.

パラメーター渡しを使用する in 場合は、一時的なローカル変数を間接的に使用することが必要になる場合があります。Some uses of in parameter passing may require indirect use of a temporary local variable:

  • in 呼び出しサイトがを使用する場合、引数は常に直接エイリアスとして渡され in ます。in arguments are always passed as direct aliases when call-site uses in. このような場合、一時は使用されません。Temporary is never used in such case.
  • in 呼び出しサイトでが使用されていない場合は、引数を直接エイリアスにする必要はありません inin arguments are not required to be direct aliases when call-site does not use in. 引数が左辺値でない場合は、一時的なを使用できます。When argument is not an LValue, a temporary may be used.
  • in パラメーターに既定値が指定されている可能性があります。in parameter may have default value. 呼び出しサイトで対応する引数を省略すると、既定値は一時的に渡されます。When corresponding argument is omitted at the call site, the default value are passed via a temporary.
  • in 引数には、id を保持しないものも含め、暗黙的な変換を含めることができます。in arguments may have implicit conversions, including those that do not preserve identity. そのような場合は、一時が使用されます。A temporary is used in those cases.
  • 通常の構造体呼び出しのレシーバーは、書き込み可能な左辺値ではない場合があります (既存の case!)。receivers of ordinary struct calls may not be writeable LValues (existing case!). そのような場合は、一時が使用されます。A temporary is used in those cases.

引数一時要素の有効期間は、呼び出しサイトの最も近いスコープに一致します。The life time of the argument temporaries matches the closest encompassing scope of the call-site.

一時変数の正式な有効期間は、参照によって返される変数のエスケープ分析に関連するシナリオで意味的に意味があります。The formal life time of temporary variables is semantically significant in scenarios involving escape analysis of variables returned by reference.

パラメーターのメタデータ表現 inMetadata representation of in parameters.

System.Runtime.CompilerServices.IsReadOnlyAttributeが byref パラメーターに適用されている場合は、パラメーターがパラメーターであることを意味し in ます。When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a byref parameter, it means that the parameter is an in parameter.

また、メソッドが abstract または virtual の場合、このようなパラメーターのシグネチャ (およびそのようなパラメーターのみ) にはが必要 modreq[System.Runtime.InteropServices.InAttribute] です。In addition, if the method is abstract or virtual, then the signature of such parameters (and only such parameters) must have modreq[System.Runtime.InteropServices.InAttribute].

動機: パラメーターをオーバーライドまたは実装するメソッドが一致するようにするために、この処理が行われ in ます。Motivation: this is done to ensure that in a case of method overriding/implementing the in parameters match.

デリゲートのメソッドにも同じ要件が適用さ Invoke れます。Same requirements apply to Invoke methods in delegates.

動機: readonly デリゲートの作成時または割り当て時に既存のコンパイラが単純に無視できないようにします。Motivation: this is to ensure that existing compilers cannot simply ignore readonly when creating or assigning delegates.

読み取り専用の参照によって返されます。Returning by readonly reference.

目的Motivation

このサブ機能の目的は、 in パラメーターのコピーを回避し、戻り側ではほぼ対称です。The motivation for this sub-feature is roughly symmetrical to the reasons for the in parameters - avoiding copying, but on the returning side. この機能を使用する前に、メソッドまたはインデクサーに2つのオプションがありました。 1) 参照によって返され、変更または2に公開されることがありますが、値によって返されます。Prior to this feature, a method or an indexer had two options: 1) return by reference and be exposed to possible mutations or 2) return by value which results in copying.

ソリューション ( ref readonly を返します)Solution (ref readonly returns)

この機能により、メンバーは変数を変更に公開せずに参照渡しで返すことができます。The feature allows a member to return variables by reference without exposing them to mutations.

宣言 ( ref readonly 戻りメンバーを)Declaring ref readonly returning members

ref readonly戻り値のシグネチャに対する修飾子の組み合わせは、メンバーが読み取り専用の参照を返すことを示すために使用されます。A combination of modifiers ref readonly on the return signature is used to to indicate that the member returns a readonly reference.

すべての目的で、 ref readonly メンバーは readonly フィールドおよびパラメーターと同様に変数として扱われ readonly in ます。For all purposes a ref readonly member is treated as a readonly variable - similar to readonly fields and in parameters.

たとえば ref readonly 、構造体型を持つメンバーのフィールドは、すべて変数として再帰的に分類され readonly ます。For example fields of ref readonly member which has a struct type are all recursively classified as readonly variables. -引数として渡すことはできますが、引数として渡すことはでき in ません ref out- It is permitted to pass them as in arguments, but not as ref or out arguments.

ref readonly Guid Method1()
{
}

Method2(in Method1()); // valid. Can pass as `in` argument.

Method3(ref Method1()); // not valid. Cannot pass as `ref` argument
  • ref readonly 戻り値は、が返されるのと同じ場所で許可され ref ます。ref readonly returns are allowed in the same places were ref returns are allowed. これには、インデクサー、デリゲート、ラムダ、ローカル関数が含まれます。This includes indexers, delegates, lambdas, local functions.

  • またはの ref / 相違点でのオーバーロードは許可されていません ref readonlyIt is not permitted to overload on ref/ref readonly / differences.

  • 通常の byval でのオーバーロードが許可され、 ref readonly 相違が返されます。It is permitted to overload on ordinary byval and ref readonly return differences.

  • OHI の目的 (オーバーロード、非表示、実装) は、 ref readonly と似ていますが、とは異なり ref ます。For the purpose of OHI (Overloading, Hiding, Implementing), ref readonly is similar but distinct from ref. たとえば、1をオーバーライドするメソッドは、 ref readonly それ自体がであり、かつ id 変換可能な型を持っている必要があり ref readonly ます。For example the a method that overrides ref readonly one, must itself be ref readonly and have identity-convertible type.

  • デリゲート/ラムダ/メソッドグループの変換のため、 ref readonly は似ていますが、とは異なり ref ます。For the purpose of delegate/lambda/method group conversions, ref readonly is similar but distinct from ref. ラムダおよび適用可能なメソッドグループ変換の候補 ref readonly は、id 変換可能な型を返すターゲットデリゲートの戻り値と一致する必要があり ref readonly ます。Lambdas and applicable method group conversion candidates have to match ref readonly return of the target delegate with ref readonly return of the type that is identity-convertible.

  • 一般的な分散のために、 ref readonly は非バリアントを返します。For the purpose of generic variance, ref readonly returns are nonvariant.

注: 戻り値には、 ref readonly 参照型またはプリミティブ型を含む警告はありません。NOTE: There are no warnings on ref readonly returns that have reference or primitives types. 一般には意味がないかもしれませんが、場合によっては、ユーザーがプリミティブをとして渡す必要があり in ます。It may be pointless in general, but in some cases user must/want to pass primitives as in. 例- ref readonly T Method() がに代入されたときのように、ジェネリックメソッドをオーバーライド T int します。Examples - overriding a generic method like ref readonly T Method() when T was substituted to be int.

戻り値が非効率的に使用された場合に警告するアナライザーがあると想定されてい ref readonly ますが、このような分析のルールは、言語仕様の一部になるにはあいまいすぎます。It is conceivable to have an analyzer that warns in cases of inefficient use of ref readonly returns, but the rules for such analysis would be too fuzzy to be a part of a language specification.

メンバーからの戻り ref readonlyReturning from ref readonly members

メソッド本体内では、構文は通常の ref 戻り値と同じです。Inside the method body the syntax is the same as with regular ref returns. は、含んでいる readonly メソッドから推論されます。The readonly will be inferred from the containing method.

これは、 return ref readonly <expression> 不必要に長い時間がかかり、常にエラーになる可能性がある部分でのみ不一致が発生することを意図してい readonly ます。The motivation is that return ref readonly <expression> is unnecessary long and only allows for mismatches on the readonly part that would always result in errors. refただし、厳密なエイリアスと値によって何らかの値が渡される他のシナリオとの一貫性を確保するためには、が必要です。The ref is, however, required for consistency with other scenarios where something is passed via strict aliasing vs. by value.

パラメーターの場合とは異なり in 、は ref readonly ローカルコピーを使用してを返すことはありません。Unlike the case with in parameters, ref readonly returns never return via a local copy. このような手法が返された直後にコピーが存在しないことを考慮すると、無意味で危険になります。Considering that the copy would cease to exist immediately upon returning such practice would be pointless and dangerous. したがって ref readonly 、は常に直接参照を返します。Therefore ref readonly returns are always direct references.

例:Example:

struct ImmutableArray<T>
{
    private readonly T[] array;

    public ref readonly T ItemRef(int i)
    {
        // returning a readonly reference to an array element
        return ref this.array[i];
    }
}

  • の引数は return ref 左辺値である必要があります (既存の規則)An argument of return ref must be an LValue (existing rule)
  • の引数は return ref "戻り値として安全" にする必要があります (既存の規則)。An argument of return ref must be "safe to return" (existing rule)
  • メンバーで ref readonly は、の引数 return ref書き込み可能である必要はありませんIn a ref readonly member an argument of return ref is not required to be writeable . たとえば、このようなメンバーは、readonly フィールドまたはそのパラメーターの1つを参照して返すことができ in ます。For example such member can ref-return a readonly field or one of its in parameters.

規則を返すことが安全です。Safe to Return rules.

参照用の規則を返す通常の安全な方法は、読み取り専用の参照にも当てはまります。Normal safe to return rules for references will apply to readonly references as well.

は、 ref readonly 通常の ref ローカル/パラメーター/戻り値から取得できますが、他の方法では取得できないことに注意してください。Note that a ref readonly can be obtained from a regular ref local/parameter/return, but not the other way around. それ以外の場合、 ref readonly 戻り値の安全性は、通常の戻り値と同じ方法で推定され ref ます。Otherwise the safety of ref readonly returns is inferred the same way as for regular ref returns.

右辺値をパラメーターとして渡すことができ、もう1つの規則が必要であることを考慮して in ref readonly ください。 右辺値は参照渡しでは安全に返すことができませんConsidering that RValues can be passed as in parameter and returned as ref readonly we need one more rule - RValues are not safe-to-return by reference.

右辺 in 値がコピーを介してパラメーターに渡され、の形式で返される場合を考えてみ ref readonly ます。Consider the situation when an RValue is passed to an in parameter via a copy and then returned back in a form of a ref readonly. 呼び出し元のコンテキストでは、このような呼び出しの結果はローカルデータへの参照であるため、を返すのは安全ではありません。In the context of the caller the result of such invocation is a reference to local data and as such is unsafe to return. 右辺値が返されない場合、既存のルールは #6 既にこのケースを処理しています。Once RValues are not safe to return, the existing rule #6 already handles this case.

例:Example:

ref readonly Vector3 Test1()
{
    // can pass an RValue as "in" (via a temp copy)
    // but the result is not safe to return
    // because the RValue argument was not safe to return by reference
    return ref Test2(default(Vector3));
}

ref readonly Vector3 Test2(in Vector3 r)
{
    // this is ok, r is returnable
    return ref r;
}

更新された safe to return 規則:Updated safe to return rules:

  1. ヒープ上の変数への参照は、安全に返すことができるrefs to variables on the heap are safe to return
  2. ref/in パラメーターは安全に返す in ことができるパラメーターは、読み取り専用としてのみ返すことができます。ref/in parameters are safe to return in parameters naturally can only be returned as readonly.
  3. out パラメーターは 、既に使用されているように、確実に返すことができます (ただし、既に割り当てられている必要があります)。out parameters are safe to return (but must be definitely assigned, as is already the case today)
  4. インスタンス構造体フィールドは、受信側が安全に戻ることができる限り、安全に返すことができるinstance struct fields are safe to return as long as the receiver is safe to return
  5. ' this ' は、構造体メンバーからは安全ではありません'this' is not safe to return from struct members
  6. 他のメソッドから返される参照は、仮パラメーターとしてそのメソッドに渡されたすべての refs/アウトが安全に返すことができた場合に、安全に返すことができます。 具体的には、受信側 が構造体、クラス、ジェネリック型パラメーターとして型指定されているかどうかに関係なく、受信側が安全に戻る場合は無関係です。a ref, returned from another method is safe to return if all refs/outs passed to that method as formal parameters were safe to return. Specifically it is irrelevant if receiver is safe to return, regardless whether receiver is a struct, class or typed as a generic type parameter.
  7. 右辺値は、参照渡しでは安全ではありません。 具体的には、右辺値をパラメーターとして渡すことが安全です。RValues are not safe to return by reference. Specifically RValues are safe to pass as in parameters.

注: ref のような型と参照の再割り当てが関係している場合は、戻り値の安全性に関する追加の規則があります。NOTE: There are additional rules regarding safety of returns that come into play when ref-like types and ref-reassignments are involved. 規則はとのメンバーにも適用 ref ref readonly されるため、ここでは説明しません。The rules equally apply to ref and ref readonly members and therefore are not mentioned here.

エイリアス動作。Aliasing behavior.

ref readonly メンバーは、通常のメンバーと同じエイリアス動作を提供し ref ます (readonly の場合を除く)。ref readonly members provide the same aliasing behavior as ordinary ref members (except for being readonly). したがって、ラムダ、非同期、反復子、stack 書き込むなどでキャプチャするために使用します。同じ制限が適用されます。Therefore for the purpose of capturing in lambdas, async, iterators, stack spilling etc... the same restrictions apply. :.- I.E. 実際の参照をキャプチャできないため、またメンバーの評価に副作用があるため、このようなシナリオは許可されません。due to inability to capture the actual references and due to side-effecting nature of member evaluation such scenarios are disallowed.

これは許可され、 ref readonly this 通常の書き込み可能な参照として使用される通常の構造体メソッドの受信側である場合に、コピーを作成する必要があります。It is permitted and required to make a copy when ref readonly return is a receiver of regular struct methods, which take this as an ordinary writeable reference. 以前は、このような呼び出しが読み取り専用変数に適用されるすべての場合、ローカルコピーが作成されていました。Historically in all cases where such invocations are applied to readonly variable a local copy is made.

メタデータ表現。Metadata representation.

System.Runtime.CompilerServices.IsReadOnlyAttributeByref を返すメソッドの戻り値にを適用すると、メソッドが読み取り専用の参照を返すことを意味します。When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to the return of a byref returning method, it means that the method returns a readonly reference.

また、このようなメソッドの結果のシグネチャ (およびこれらのメソッドのみ) には、が必要 modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute] です。In addition, the result signature of such methods (and only those methods) must have modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute].

動機: readonly 戻り値を使用してメソッドを呼び出すときに、既存のコンパイラが単純に無視できないようにします。 ref readonlyMotivation: this is to ensure that existing compilers cannot simply ignore readonly when invoking methods with ref readonly returns

Readonly 構造体Readonly structs

Short- this コンストラクターを除く、構造体のすべてのインスタンスメンバーのパラメーターを作成する機能 inIn short - a feature that makes this parameter of all instance members of a struct, except for constructors, an in parameter.

目的Motivation

コンパイラは、構造体インスタンスでメソッドを呼び出すとインスタンスが変更される可能性があると想定する必要があります。Compiler must assume that any method call on a struct instance may modify the instance. 実際には、書き込み可能な参照は、パラメーターとしてメソッドに渡され、 this この動作を完全に有効にします。Indeed a writeable reference is passed to the method as this parameter and fully enables this behavior. 変数に対してこのような呼び出しを許可するために readonly 、呼び出しは一時コピーに適用されます。To allow such invocations on readonly variables, the invocations are applied to temp copies. にくいになる可能性があります。また、パフォーマンス上の理由から、強制的に破棄することもでき readonly ます。That could be unintuitive and sometimes forces people to abandon readonly for performance reasons.
例: https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/Example: https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

パラメーターのサポートを追加した後に、 in ref readonly 読み取り専用の変数がより一般的になるため、防御的なコピーの問題が悪化します。After adding support for in parameters and ref readonly returns the problem of defensive copying will get worse since readonly variables will become more common.

解決策Solution

readonly this in コンストラクターを除くすべての構造体インスタンスメソッドでパラメーターとして扱われる構造体宣言で修飾子を使用できます。Allow readonly modifier on struct declarations which would result in this being treated as in parameter on all struct instance methods except for constructors.

static void Test(in Vector3 v1)
{
    // no need to make a copy of v1 since Vector3 is a readonly struct
    System.Console.WriteLine(v1.ToString());
}

readonly struct Vector3
{
    . . .

    public override string ToString()
    {
        // not OK!!  `this` is an `in` parameter
        foo(ref this.X);

        // OK
        return $"X: {X}, Y: {Y}, Z: {Z}";
    }
}

Readonly 構造体のメンバーに関する制限事項Restrictions on members of readonly struct

  • 読み取り専用の構造体のインスタンスフィールドは読み取り専用である必要があります。Instance fields of a readonly struct must be readonly.
    動機: 外部にのみ書き込むことができますが、メンバーを経由することはできません。Motivation: can only be written to externally, but not through members.
  • 読み取り専用の構造体のインスタンス autoproperties は、get 専用である必要があります。Instance autoproperties of a readonly struct must be get-only.
    動機: インスタンスフィールドに対する制限の結果。Motivation: consequence of restriction on instance fields.
  • Readonly 構造体は、フィールドのようなイベントを宣言することはできません。Readonly struct may not declare field-like events.
    動機: インスタンスフィールドに対する制限の結果。Motivation: consequence of restriction on instance fields.

メタデータ表現。Metadata representation.

System.Runtime.CompilerServices.IsReadOnlyAttributeが値型に適用される場合は、型がであることを意味し readonly struct ます。When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a value type, it means that the type is a readonly struct.

特に次の点に違いがあります。In particular:

  • 型の id IsReadOnlyAttribute は重要ではありません。The identity of the IsReadOnlyAttribute type is unimportant. 実際には、必要に応じて、それを含んでいるアセンブリのコンパイラによって埋め込むことができます。In fact it can be embedded by the compiler in the containing assembly if needed.

ref/in 拡張メソッドref/in extension methods

実際には、既存の提案 ( https://github.com/dotnet/roslyn/issues/165) および対応するプロトタイプ PR () があり https://github.com/dotnet/roslyn/pull/15650) ます。There is actually an existing proposal (https://github.com/dotnet/roslyn/issues/165) and corresponding prototype PR (https://github.com/dotnet/roslyn/pull/15650). このアイデアがまったく新しいものではないことを確認したいだけです。I just want to acknowledge that this idea is not entirely new. ただし、ここでは、 ref readonly RValue レシーバーで何を行うかについて、悪化の問題を洗練されていないため、ここで関連しています。It is, however, relevant here since ref readonly elegantly removes the most contentious issue about such methods - what to do with RValue receivers.

一般的な考え方は、 this 型が構造体型であることがわかっている限り、拡張メソッドが参照渡しでパラメーターを受け取ることができるようにすることです。The general idea is allowing extension methods to take the this parameter by reference, as long as the type is known to be a struct type.

public static void Extension(ref this Guid self)
{
    // do something
}

このような拡張メソッドを作成する理由は主に次のとおりです。The reasons for writing such extension methods are primarily:

  1. レシーバーが大きな構造体である場合はコピーしないAvoid copying when receiver is a large struct
  2. 構造体の拡張メソッドの変更を許可するAllow mutating extension methods on structs

クラスでこれを許可しない理由The reasons why we do not want to allow this on classes

  1. その目的は非常に限られています。It would be of very limited purpose.
  2. これは、メソッドの呼び出しが呼び出し後に非受信者を変更することができないという長い不変性を損なう null 可能性が null あります。It would break long standing invariant that a method call cannot turn non-null receiver to become null after invocation.

実際に null null は、またはによって 明示的 に割り当てられているか、渡されていない限り、現在の変数はになりません ref outIn fact, currently a non-null variable cannot become null unless explicitly assigned or passed by ref or out. 読みやすくするため、またはその他の形式の "ここでは null にすることができます" 分析が非常に役立ちます。That greatly aids readability or other forms of "can this be a null here" analysis. 3. Null 条件付きアクセスの "評価1回のみ" セマンティクスによって調整するのは困難です。It would be hard to reconcile with "evaluate once" semantics of null-conditional accesses. 例: obj.stringField?.RefExtension(...) -のコピーをキャプチャし stringField て null チェックを意味を持つようにする必要がありますが、refextension 内への代入は this フィールドに反映されません。Example: obj.stringField?.RefExtension(...) - need to capture a copy of stringField to make the null check meaningful, but then assignments to this inside RefExtension would not be reflected back to the field.

参照渡しで最初の引数を受け取る 構造体 に対して拡張メソッドを宣言する機能は、長時間要求でした。An ability to declare extension methods on structs that take the first argument by reference was a long-standing request. ブロックの考慮事項の1つは、"レシーバーが左辺値ではない場合の動作" です。One of the blocking consideration was "what happens if receiver is not an LValue?".

  • すべての拡張メソッドを静的メソッドとして呼び出すこともできます (あいまいさを解決する唯一の方法である場合もあります)。There is a precedent that any extension method could also be called as a static method (sometimes it is the only way to resolve ambiguity). これは、右辺値レシーバーを許可しないことを指定します。It would dictate that RValue receivers should be disallowed.
  • 一方、構造体のインスタンスメソッドが関係する場合は、同様の状況でコピーに対して呼び出しを行う方法もあります。On the other hand there is a practice of making invocation on a copy in similar situations when struct instance methods are involved.

"暗黙的なコピー" が存在する理由は、構造体のメソッドの大部分は実際には構造体を変更せず、を示すことができないためです。The reason why the "implicit copying" exists is because the majority of struct methods do not actually modify the struct while not being able to indicate that. そのため、最も実用的な解決策は、コピーでの呼び出しを行うだけでしたが、この方法は、パフォーマンスの低下やバグの原因となっていることがわかっています。Therefore the most practical solution was to just make the invocation on a copy, but this practice is known for harming performance and causing bugs.

パラメーターを使用できるようになったため、拡張機能によって in 意図が通知される可能性があります。Now, with availability of in parameters, it is possible for an extension to signal the intent. そのため、拡張機能が書き込み可能な受信側で呼び出されるようにすることで、難問を解決できますが、拡張機能は必要に応じて ref in 暗黙のコピーを許可します。Therefore the conundrum can be resolved by requiring ref extensions to be called with writeable receivers while in extensions permit implicit copying if necessary.

// this can be called on either RValue or an LValue
public static void Reader(in this Guid self)
{
    // do something nonmutating.
    WriteLine(self == default(Guid));
}

// this can be called only on an LValue
public static void Mutator(ref this Guid self)
{
    // can mutate self
    self = new Guid();
}

in 拡張機能とジェネリック。in extensions and generics.

拡張メソッドの目的は、 ref 受信側を直接変更するか、または変化するメンバーを呼び出すことです。The purpose of ref extension methods is to mutate the receiver directly or by invoking mutating members. したがって ref this T T 、は、構造体として制約されている限り、拡張が許可されます。Therefore ref this T extensions are allowed as long as T is constrained to be a struct.

一方、では、 in 暗黙的なコピーを減らすために、特別な拡張メソッドが存在します。On the other hand in extension methods exist specifically to reduce implicit copying. ただし、パラメーターの使用は、 in T インターフェイスメンバーを介して行う必要があります。However any use of an in T parameter will have to be done through an interface member. すべてのインターフェイスメンバーは変化していると見なされるため、このような使用ではコピーが必要になります。Since all interface members are considered mutating, any such use would require a copy. -コピーを減らすのではなく、その逆の効果が得られます。- Instead of reducing copying, the effect would be the opposite. したがって in this T T 、が制約に関係なくジェネリック型パラメーターである場合、は許可されません。Therefore in this T is not allowed when T is a generic type parameter regardless of constraints.

有効な拡張メソッドの種類 (要約):Valid kinds of extension methods (recap):

拡張メソッドの次の形式の this 宣言が許可されるようになりました。The following forms of this declaration in an extension method are now allowed:

  1. this T arg -通常の byval 拡張。this T arg - regular byval extension. (既存のケース)(existing case)
  • T には、参照型または型パラメーターを含む任意の型を指定できます。T can be any type, including reference types or type parameters. インスタンスは、呼び出しの後に同じ変数になります。Instance will be the same variable after the call. この引数変換 の種類の暗黙的な変換を許可します。Allows implicit conversions of this-argument-conversion kind. 右辺値で呼び出すことができます。Can be called on RValues.

  • in this T self - in 番号.in this T self - in extension. T は実際の構造体型である必要があります。T must be an actual struct type. インスタンスは、呼び出しの後に同じ変数になります。Instance will be the same variable after the call. この引数変換 の種類の暗黙的な変換を許可します。Allows implicit conversions of this-argument-conversion kind. 右辺値で呼び出すことができます (必要に応じて、一時で呼び出すことができます)。Can be called on RValues (may be invoked on a temp if needed).

  • ref this T self - ref 番号.ref this T self - ref extension. T は構造体型であるか、または構造体として制約されるジェネリック型パラメーターである必要があります。T must be a struct type or a generic type parameter constrained to be a struct. インスタンスは、呼び出しによって書き込まれる場合があります。Instance may be written to by the invocation. Id 変換のみを許可します。Allows only identity conversions. 書き込み可能な左辺値で呼び出す必要があります。Must be called on writeable LValue. (temp 経由では呼び出されません)。(never invoked via a temp).

Readonly ref ローカル変数。Readonly ref locals.

動機.Motivation.

ref readonlyメンバーが導入されると、適切な種類のローカルとペアにする必要があることを明確にしました。Once ref readonly members were introduced, it was clear from the use that they need to be paired with appropriate kind of local. メンバーを評価すると副作用が生じる可能性があります。したがって、結果を複数回使用する必要がある場合は、格納する必要があります。Evaluation of a member may produce or observe side effects, therefore if the result must be used more than once, it needs to be stored. 通常 ref のローカルローカル変数は、参照を割り当てることができないため、ここでは役に立ちません readonlyOrdinary ref locals do not help here since they cannot be assigned a readonly reference.

Solution.Solution.

ref readonlyローカルの宣言を許可します。Allow declaring ref readonly locals. これは、書き込み可能でない新しい種類の ref ローカルローカルです。This is a new kind of ref locals that is not writeable. その結果、 ref readonly ローカル変数は、書き込みにこれらの変数を公開せずに、読み取り専用変数への参照を受け入れることができます。As a result ref readonly locals can accept references to readonly variables without exposing these variables to writes.

ref readonlyローカル変数の宣言と使用Declaring and using ref readonly locals.

このようなローカルの構文では、 ref readonly 宣言サイトで修飾子を使用します (この順序で指定します)。The syntax of such locals uses ref readonly modifiers at declaration site (in that specific order). 通常 ref のローカル変数と同様に、 ref readonly ローカル変数は宣言時に ref 初期化される必要があります。Similarly to ordinary ref locals, ref readonly locals must be ref-initialized at declaration. 通常のローカル変数とは異なり、 ref ref readonly ローカルでは、 readonly in パラメーター、フィールド、メソッドなどの左辺値を参照でき readonly ref readonly ます。Unlike regular ref locals, ref readonly locals can refer to readonly LValues like in parameters, readonly fields, ref readonly methods.

すべての目的で、 ref readonly ローカルは変数として扱われ readonly ます。For all purposes a ref readonly local is treated as a readonly variable. 使用に関する制限事項のほとんどは、 readonly フィールドまたはパラメーターの場合と同じ in です。Most of the restrictions on the use are the same as with readonly fields or in parameters.

たとえば、 in 構造体型を持つパラメーターのフィールドは、すべて変数として再帰的に分類され readonly ます。For example fields of an in parameter which has a struct type are all recursively classified as readonly variables .

static readonly ref Vector3 M1() => . . .

static readonly ref Vector3 M1_Trace()
{
    // OK
    ref readonly var r1 = ref M1();

    // Not valid. Need an LValue
    ref readonly Vector3 r2 = ref default(Vector3);

    // Not valid. r1 is readonly.
    Mutate(ref r1);

    // OK.
    Print(in r1);

    // OK.
    return ref r1;
}

ローカルの使用に関する制限事項 ref readonlyRestrictions on use of ref readonly locals

ローカルの性質を除けば readonlyref readonly ローカルは通常のローカルのように動作 ref し、まったく同じ制限が適用されます。Except for their readonly nature, ref readonly locals behave like ordinary ref locals and are subject to exactly same restrictions.
クロージャでのキャプチャに関連する制限の例として、 async メソッドまたは分析での宣言は、 safe-to-return ローカルにも同様に適用さ ref readonly れます。For example restrictions related to capturing in closures, declaring in async methods or the safe-to-return analysis equally applies to ref readonly locals.

三項 ref 式。Ternary ref expressions. ("条件付き左辺値" とも呼ばれる)(aka "Conditional LValues")

目的Motivation

およびローカルのを使用する ref と、 ref readonly 条件に基づいて、1つまたは別のターゲット変数を使用して、そのローカル変数を参照している必要があります。Use of ref and ref readonly locals exposed a need to ref-initialize such locals with one or another target variable based on a condition.

一般的な回避策は、次のようなメソッドを導入することです。A typical workaround is to introduce a method like:

ref T Choice(bool condition, ref T consequence, ref T alternative)
{
    if (condition)
    {
         return ref consequence;
    }
    else
    {
         return ref alternative;
    }
}

Choiceは、すべて の引数が呼び出しサイトで評価される必要があるため、にくいの動作とバグにつながるため、は三項の完全な置換ではないことに注意してください。Note that Choice is not an exact replacement of a ternary since all arguments must be evaluated at the call site, which was leading to unintuitive behavior and bugs.

次のものは期待どおりに機能しません。The following will not work as expected:

    // will crash with NRE because 'arr[0]' will be executed unconditionally
    ref var r = ref Choice(arr != null, ref arr[0], ref otherArr[0]);

解決策Solution

条件に基づいて左辺値の引数のいずれかへの参照に評価される特殊な種類の条件式を許可します。Allow special kind of conditional expression that evaluates to a reference to one of LValue argument based on a condition.

三項式を使用し ref ます。Using ref ternary expression.

ref条件式のフレーバーの構文は、<condition> ? ref <consequence> : ref <alternative>;The syntax for the ref flavor of a conditional expression is <condition> ? ref <consequence> : ref <alternative>;

通常の条件式と同じように、 <consequence> または <alternative> ブール条件式の結果に応じて評価されます。Just like with the ordinary conditional expression only <consequence> or <alternative> is evaluated depending on result of the boolean condition expression.

通常の条件式とは異なり、 ref 条件式:Unlike ordinary conditional expression, ref conditional expression:

  • <consequence>とが左辺値である必要があり <alternative> ます。requires that <consequence> and <alternative> are LValues.
  • ref 条件式自体は左辺値であり、ref conditional expression itself is an LValue and
  • ref との両方 <consequence> <alternative> が書き込み可能な値である場合、条件式は書き込み可能ですref conditional expression is writeable if both <consequence> and <alternative> are writeable LValues

例 :Examples:
ref 三項は左辺値であるため、参照渡しで渡すことができます。ref ternary is an LValue and as such it can be passed/assigned/returned by reference;

     // pass by reference
     foo(ref (arr != null ? ref arr[0]: ref otherArr[0]));

     // return by reference
     return ref (arr != null ? ref arr[0]: ref otherArr[0]);

左辺値である場合は、に割り当てることもできます。Being an LValue, it can also be assigned to.

     // assign to
     (arr != null ? ref arr[0]: ref otherArr[0]) = 1;

     // error. readOnlyField is readonly and thus conditional expression is readonly
     (arr != null ? ref arr[0]: ref obj.readOnlyField) = 1;

は、メソッド呼び出しの受信者として使用でき、必要に応じてコピーをスキップします。Can be used as a receiver of a method call and skip copying if necessary.

     // no copies
     (arr != null ? ref arr[0]: ref otherArr[0]).StructMethod();

     // invoked on a copy.
     // The receiver is `readonly` because readOnlyField is readonly.
     (arr != null ? ref arr[0]: ref obj.readOnlyField).StructMethod();

     // no copies. `ReadonlyStructMethod` is a method on a `readonly` struct
     // and can be invoked directly on a readonly receiver
     (arr != null ? ref arr[0]: ref obj.readOnlyField).ReadonlyStructMethod();

ref 三項は、通常の (非 ref) コンテキストでも使用できます。ref ternary can be used in a regular (not ref) context as well.

     // only an example
     // a regular ternary could work here just the same
     int x = (arr != null ? ref arr[0]: ref otherArr[0]);

デメリットDrawbacks

参照と読み取り専用参照の拡張サポートに対する2つの主な引数を確認できます。I can see two major arguments against enhanced support for references and readonly references:

  1. ここで解決される問題は、非常に古いものです。The problems that are solved here are very old. これは、特に既存のコードが役に立ちません。Why suddenly solve them now, especially since it would not help existing code?

新しいドメインで使用されている C# と .Net が見つかると、いくつかの問題が目立つようになります。As we find C# and .Net used in new domains, some problems become more prominent.
計算オーバーヘッドに関する平均よりも重要な環境の例としては、As examples of environments that are more critical than average about computation overheads, I can list

  • 計算が請求され、応答性が高いクラウド/データセンターのシナリオは、競争上の利点です。cloud/datacenter scenarios where computation is billed for and responsiveness is a competitive advantage.
  • 待機時間に関するソフトリアルタイムの要件を持つゲーム/VR/ARGames/VR/AR with soft-realtime requirements on latencies

この機能では、タイプセーフなどの既存の長所を一切犠牲にすることはできませんが、一般的なシナリオではオーバーヘッドを低く抑えることができます。This feature does not sacrifice any of the existing strengths such as type-safety, while allowing to lower overheads in some common scenarios.

  1. コントラクトを使用すると、呼び出し先がルールによって再生されることを合理的に保証でき readonly ますか。Can we reasonably guarantee that the callee will play by the rules when it opts into readonly contracts?

を使用する場合は、同様の信頼があり out ます。We have similar trust when using out. の不適切な実装に out よって、未指定の動作が発生することがありますが、実際にはほとんど発生しません。Incorrect implementation of out can cause unspecified behavior, but in reality it rarely happens.

正式な検証規則を理解することで、 ref readonly 信頼の問題をさらに軽減することができます。Making the formal verification rules familiar with ref readonly would further mitigate the trust issue.

代替Alternatives

競合の主な設計は、実際には "do nothing" です。The main competing design is really "do nothing".

未解決の質問Unresolved questions

会議のデザインDesign meetings

https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-02-22.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-01.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-08-28.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-25.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-27.mdhttps://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-02-22.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-01.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-08-28.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-25.md https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-27.md