読み取り専用の参照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 構造体readonly 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)

out パラメーターと同様に、in パラメーターは、呼び出し先からの追加の保証を持つマネージ参照として渡されます。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
  • inout と組み合わせて使用することも、out をと組み合わせることもできません。in is not allowed in combination with out or with anything that out does not combine with.

  • ref/outのオーバーロードは許可されていません。 in の相違点 /。It is not permitted to overload on ref/out/in differences.

  • 通常の byval でのオーバーロードと in の違いが許可されます。It is permitted to overload on ordinary byval and in differences.

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

  • デリゲート/ラムダ/メソッドグループ変換のために、inout パラメーターと同様に動作します。For the purpose of delegate/lambda/method group conversions, in behaves similarly to an out parameter. ラムダと適用可能なメソッドグループ変換の候補は、ターゲットデリゲートのパラメーターと、id 変換可能な型の in パラメーターを in 一致させる必要があります。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. 例-Tintに置き換えられたとき、またはのようなメソッドがあるときに、Method(in T param) などのジェネリックメソッドをオーバーライドすると、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)

引数を in パラメーターに渡すには、2つの方法があります。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 は、 _id に変換_できません 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.

  • まれに、同じ呼び出しのオペランドとして使用される await 式によって in の引数がスタックに含まれる必要がある場合は、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.

このシナリオの目的は、Api のパラメーターを in、直接参照として引数を渡すことができない場合 (例: リテラル、計算結果、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 など、場合によっては、in を指定する構文的な方法はありません。In addition, in some cases, such as invocation of operators, or in extension methods, there is no syntactical way to specify in at all. それだけでは、in パラメーターに一致するときに通常の byval 引数の動作を指定する必要があります。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. 場合によっては、引数への直接参照を指定することはできないので、引数の値のコピーは代わりに使用され awaitIn 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

refout 変数と同じように、in 変数は既存の場所への参照または別名です。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が使用されていない場合、in の引数を直接エイリアスにする必要はありません。in 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.

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

Byref パラメーターに System.Runtime.CompilerServices.IsReadOnlyAttribute が適用されている場合は、パラメーターが 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 フィールドや in パラメーターと同様に readonly 変数として扱われます。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 readonly/相違点でのオーバーロードは許可されていません。It 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. たとえば、ref readonly 1 をオーバーライドするメソッドは、それ自体が ref readonly で、id 変換可能な型を持つ必要があります。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. ラムダおよび適用可能なメソッドグループ変換の候補は、id 変換可能な型の ref readonly 戻り値を使用して、ターゲットデリゲートの 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. 例-T が置換されたときに、ref readonly T Method() などのジェネリックメソッドをオーバーライドして 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 readonly メンバーからの戻りReturning 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 の引数は "return to return" (既存のルール) にする必要があります。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 フィールドまたはその in パラメーターの1つを参照して返すことができます。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.

右辺値を in パラメーターとして渡し、ref readonly として返すことを考慮すると、もう1つの規則が必要になります。これは、参照渡しでは安全に返すことができません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. これらの規則は refref 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.

これは許可されており、通常の書き込み可能な参照として this を受け取る通常の構造体メソッドの受信側で ref readonly が返される場合に、コピーを作成する必要があります。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.

Byref を返すメソッドの戻り値に System.Runtime.CompilerServices.IsReadOnlyAttribute が適用されている場合は、メソッドが読み取り専用の参照を返すことを意味します。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].

動機: ref readonly が返されたメソッドを呼び出すときに、既存のコンパイラが単に readonly を無視できないようにするためです。Motivation: this is to ensure that existing compilers cannot simply ignore readonly when invoking methods with ref readonly returns

Readonly 構造体Readonly structs

Short-コンストラクターを除き、構造体のすべてのインスタンスメンバーの this パラメーターを作成する機能。これは、in パラメーターです。In 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

コンストラクターを除くすべての構造体インスタンスメソッドで、thisin パラメーターとして処理することになる構造体宣言で readonly 修飾子を許可します。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:

  • IsReadOnlyAttribute 型の id は重要ではありません。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.

実際には、ref または outによって_明示的_に割り当てられていない限り、null ない変数を null にすることはできません。In 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. したがって、T が構造体として制約されている限り、ref this 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. したがって、制約に関係なく T がジェネリック型パラメーターである場合、in this 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 拡張機能を in this T selfします。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 拡張機能を ref this T selfします。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 ローカルは、readonly 参照を割り当てることができないため、ここでは役に立ちません。Ordinary 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 ローカルは in パラメーター、readonly フィールド、ref readonly メソッドなどの readonly LValues を参照できます。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 readonly ローカルの使用に関する制限事項Restrictions on use of ref readonly locals

readonly の性質を除き、ref 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

refref readonly の使用は、条件に基づいて1つまたは別のターゲット変数を使用して、ローカルの参照を ref に初期化する必要があります。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>;

通常の条件式の場合と同様に、ブール条件式の結果に応じて <alternative> が評価されるのは、<consequence> の場合のみです。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
  • <consequence><alternative> の両方が書き込み可能な値である場合 ref 条件式は書き込み可能です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