Referencias readonlyReadonly references

  • [x] propuesto[x] Proposed
  • [x] prototipo[x] Prototype
  • [x] implementación: iniciada[x] Implementation: Started
  • [] Especificación: no iniciada[ ] Specification: Not Started

ResumenSummary

La característica "referencias de solo lectura" es realmente un grupo de características que aprovechan la eficacia de pasar variables por referencia, pero sin exponer los datos a modificaciones: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 losin parameters
  • Devoluciones de ref readonlyref readonly returns
  • readonly Structsreadonly structs
  • ref/in métodos de extensiónref/in extension methods
  • ref readonly variables localesref readonly locals
  • ref expresiones condicionalesref conditional expressions

Pasar argumentos como referencias de solo lectura.Passing arguments as readonly references.

Existe una propuesta existente que toca este tema https://github.com/dotnet/roslyn/issues/115 como un caso especial de parámetros de solo lectura sin entrar en muchos detalles.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. Aquí solo quiero reconocer que la idea por sí misma no es muy nueva.Here I just want to acknowledge that the idea by itself is not very new.

MotivaciónMotivation

Antes de esta característica, C# no tenía una manera eficaz de expresar el deseo de pasar variables de struct a llamadas a métodos con fines de solo lectura sin intención de modificar.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. El paso normal de los argumentos por valor implica la copia, lo que agrega costos innecesarios.Regular by-value argument passing implies copying, which adds unnecessary costs. Que dirige a los usuarios para que usen el paso de argumentos por referencia y que se basen en comentarios o documentación para indicar que no se supone que el destinatario de la llamada modifique los datos.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. No es una buena solución por muchas razones.It is not a good solution for many reasons.
Los ejemplos son numerosos operadores matemáticos-vector/Matrix en las bibliotecas de gráficos, como XNA , se sabe que tienen operandos Ref únicamente debido a consideraciones de rendimiento.The examples are numerous - vector/matrix math operators in graphics libraries like XNA are known to have ref operands purely because of performance considerations. Hay código en el propio compilador Roslyn que usa estructuras para evitar asignaciones y, a continuación, los pasa por referencia para evitar la copia de costos.There is code in Roslyn compiler itself that uses structs to avoid allocations and then passes them by reference to avoid copying costs.

Solución ( in parámetros)Solution (in parameters)

De forma similar a los out parámetros, los in parámetros se pasan como referencias administradas con garantías adicionales del destinatario.Similarly to the out parameters, in parameters are passed as managed references with additional guarantees from the callee.
A diferencia de los out parámetros que debe asignar el destinatario antes de cualquier otro uso, in el destinatario no puede asignar los parámetros.Unlike out parameters which must be assigned by the callee before any other use, in parameters cannot be assigned by the callee at all.

Como resultado, in los parámetros permiten la eficacia del paso de argumentos indirectos sin exponer argumentos a mutaciones por parte del destinatario.As a result in parameters allow for effectiveness of indirect argument passing without exposing arguments to mutations by the callee.

Declaración de parámetros inDeclaring in parameters

in los parámetros se declaran mediante in la palabra clave como modificador en la Signatura del parámetro.in parameters are declared by using in keyword as a modifier in the parameter signature.

Para todos los propósitos in , el parámetro se trata como una readonly variable.For all purposes the in parameter is treated as a readonly variable. La mayoría de las restricciones sobre el uso de in parámetros dentro del método son las mismas que con readonly los campos.Most of the restrictions on the use of in parameters inside the method are the same as with readonly fields.

En realidad, un in parámetro puede representar un readonly campo.Indeed an in parameter may represent a readonly field. La similitud de las restricciones no es una coincidencia.Similarity of restrictions is not a coincidence.

Por ejemplo, los campos de un in parámetro que tiene un tipo de estructura se clasifican de forma recursiva como readonly variables.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 los parámetros se permiten en cualquier lugar donde se permitan los parámetros de ByVal normales.in parameters are allowed anywhere where ordinary byval parameters are allowed. Esto incluye indizadores, operadores (incluidas las conversiones), delegados, expresiones lambda, funciones locales.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 no se permite en combinación con out o con algo que out no se combina con.in is not allowed in combination with out or with anything that out does not combine with.

  • No se permite la sobrecarga de ref / out / in diferencias.It is not permitted to overload on ref/out/in differences.

  • Se permite sobrecargar en las diferencias y ByVal normales in .It is permitted to overload on ordinary byval and in differences.

  • Para el propósito de OHI (sobrecargar, ocultar, implementar), in se comporta de forma similar a un out parámetro.For the purpose of OHI (Overloading, Hiding, Implementing), in behaves similarly to an out parameter. Se aplican las mismas reglas.All the same rules apply. Por ejemplo, el método de reemplazo tendrá que coincidir in con los parámetros con in los parámetros de un tipo de identidad que se pueda convertir.For example the overriding method will have to match in parameters with in parameters of an identity-convertible type.

  • Para el propósito de las conversiones de grupo de delegado/lambda/método, in se comporta de forma similar a un out parámetro.For the purpose of delegate/lambda/method group conversions, in behaves similarly to an out parameter. Las expresiones lambda y los candidatos de conversión de grupos de métodos aplicables tendrán que coincidir con los in parámetros del delegado de destino con in parámetros de un tipo que se pueda convertir en identidades.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.

  • Para el propósito de la varianza genérica, in los parámetros son no variantes.For the purpose of generic variance, in parameters are nonvariant.

Nota: no hay advertencias en in los parámetros que tienen tipos de referencia o primitivos.NOTE: There are no warnings on in parameters that have reference or primitives types. Puede que no sea puntual en general, pero en algunos casos el usuario debe/desea pasar primitivos como in .It may be pointless in general, but in some cases user must/want to pass primitives as in. Ejemplos: invalidar un método genérico como Method(in T param) cuando T se sustituyó por int o cuando tiene métodos como 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)

Es imaginable tener un analizador que advierte en los casos de uso ineficaz de in los parámetros, pero las reglas para dicho análisis serían demasiado aproximadas para formar parte de una especificación de lenguaje.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.

Uso de in en sitios de llamada.Use of in at call sites. ( in argumentos)(in arguments)

Hay dos maneras de pasar argumentos a in parámetros.There are two ways to pass arguments to in parameters.

in los argumentos pueden coincidir con in los parámetros:in arguments can match in parameters:

Un argumento con un in modificador en el sitio de llamada puede coincidir con in parámetros.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 el argumento debe ser un valor l (*) legible .in argument must be a readable LValue(*). Ejemplo: M1(in 42) no es válidoExample: M1(in 42) is invalid

(*) La noción de valor l/RValue varía entre los lenguajes.(*) The notion of LValue/RValue vary between languages.
Aquí, por valor l I significa una expresión que representa una ubicación a la que se puede hacer referencia directamente.Here, by LValue I mean an expression that represent a location that can be referred to directly. Y RValue significa una expresión que produce un resultado temporal que no se conserva por sí mismo.And RValue means an expression that yields a temporary result which does not persist on its own.

  • En concreto, es válido pasar readonly campos, in parámetros u otras variables formal readonly como in argumentos.In particular it is valid to pass readonly fields, in parameters or other formally readonly variables as in arguments. Ejemplo: dictionary[in Guid.Empty] es legal.Example: dictionary[in Guid.Empty] is legal. Guid.Empty es un campo de solo lectura estático.Guid.Empty is a static readonly field.

  • in el argumento debe tener la identidad de tipo que se pueda convertir al tipo del parámetro.in argument must have type identity-convertible to the type of the parameter. Ejemplo: M1<object>(in Guid.Empty) no es válido.Example: M1<object>(in Guid.Empty) is invalid. Guid.Empty no se pueden convertir las identidades en objectGuid.Empty is not identity-convertible to object

La motivación de las reglas anteriores es que los in argumentos garantizan el alias de la variable de argumento.The motivation for the above rules is that in arguments guarantee aliasing of the argument variable. El destinatario siempre recibe una referencia directa a la misma ubicación que representa el argumento.The callee always receives a direct reference to the same location as represented by the argument.

  • en situaciones excepcionales en las que los in argumentos se deben desbordar por pila debido a que las await expresiones se usan como operandos de la misma llamada, el comportamiento es el mismo que con los out ref argumentos y. Si la variable no se puede desbordar de manera referencial, se muestra un error.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.

Ejemplos:Examples:

  1. M1(in staticField, await SomethingAsync()) es válido.M1(in staticField, await SomethingAsync()) is valid. staticField es un campo estático al que se puede tener acceso más de una vez sin efectos secundarios observables.staticField is a static field which can be accessed more than once without observable side effects. Por lo tanto, se pueden proporcionar el orden de los efectos secundarios y los requisitos de alias.Therefore both the order of side effects and aliasing requirements can be provided.
  2. M1(in RefReturningMethod(), await SomethingAsync()) producirá un error.M1(in RefReturningMethod(), await SomethingAsync()) will produce an error. RefReturningMethod() es un ref método que se devuelve.RefReturningMethod() is a ref returning method. Una llamada al método puede tener efectos secundarios observables, por lo que debe evaluarse antes que el SomethingAsync() operando.A method call may have observable side effects, therefore it must be evaluated before the SomethingAsync() operand. Sin embargo, el resultado de la invocación es una referencia que no se puede conservar en el await punto de suspensión, lo que hace imposible el requisito de referencia directa.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.

Nota: los errores de desbordamiento de pila se consideran limitaciones específicas de la implementación.NOTE: the stack spilling errors are considered to be implementation-specific limitations. Por lo tanto, no tienen efecto en la resolución de sobrecarga o la inferencia lambda.Therefore they do not have effect on overload resolution or lambda inference.

Los argumentos ByVal normales pueden coincidir con in los parámetros:Ordinary byval arguments can match in parameters:

Los argumentos normales sin modificadores pueden coincidir con in los parámetros.Regular arguments without modifiers can match in parameters. En tal caso, los argumentos tienen las mismas restricciones distendidas que los argumentos ByVal normales.In such case the arguments have the same relaxed constraints as an ordinary byval arguments would have.

La motivación de este escenario es que los in parámetros de las API pueden dar lugar a inconvenientes para el usuario cuando los argumentos no se pueden pasar como una referencia directa: por ejemplo, los literales, los resultados calculados o await -Ed o los argumentos que se producen para tener tipos más específicos.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.
Todos estos casos tienen una solución trivial de almacenar el valor del argumento en una local temporal del tipo adecuado y pasar ese local como in argumento.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.
Para reducir la necesidad de este compilador de código reutilizable, puede realizar la misma transformación, si es necesario, cuando in el modificador no está presente en el sitio de llamada.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.

Además, en algunos casos, como la invocación de operadores o métodos de in extensión, no hay ninguna manera sintáctica de especificar in en absoluto.In addition, in some cases, such as invocation of operators, or in extension methods, there is no syntactical way to specify in at all. Esto solo requiere especificar el comportamiento de los argumentos ByVal normales cuando coinciden con in los parámetros.That alone requires specifying the behavior of ordinary byval arguments when they match in parameters.

En concreto:In particular:

  • es válido para pasar RValues.it is valid to pass RValues. En tal caso, se pasa una referencia a un temporal.A reference to a temporary is passed in such case. Ejemplo:Example:
Print("hello");      // not an error.

void Print<T>(in T x)
{
  //. . .
}
  • se permiten las conversiones implícitas.implicit conversions are allowed.

En realidad, se trata de un caso especial de pasar un valor rThis is actually a special case of passing an RValue

En ese caso, se pasa una referencia a un valor convertido temporal que contiene.A reference to a temporary holding converted value is passed in such case. Ejemplo:Example:

Print<int>(Short.MaxValue)     // not an error.
  • en un caso de un receptor de un in método de extensión (a diferencia de ref los métodos de extensión), se permiten las conversiones de RValues o implícitas.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. En ese caso, se pasa una referencia a un valor convertido temporal que contiene.A reference to a temporary holding converted value is passed in such case. Ejemplo:Example:
public static IEnumerable<T> Concat<T>(in this (IEnumerable<T>, IEnumerable<T>) arg)  => . . .;

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

ref / in En este documento se proporciona más información sobre los métodos de extensión.More information on ref/in extension methods is provided further in this document.

  • el desbordamiento de argumentos debido a await operandos podría sobrepasar "por valor", si es necesario.argument spilling due to await operands could spill "by-value", if necessary. En los escenarios en los que no es posible proporcionar una referencia directa al argumento, en await su lugar se vuelca una copia del valor del argumento.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.
    Ejemplo:Example:
M1(RefReturningMethod(), await SomethingAsync())   // not an error.

Dado que el resultado de una invocación de efectos secundarios es una referencia que no se puede conservar en la await suspensión, se conservará en su lugar un temporal que contenga el valor real (tal como lo haría en un caso de parámetro ByVal normal).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).

Argumentos opcionales omitidosOmitted optional arguments

Se permite in que un parámetro especifique un valor predeterminado.It is permitted for an in parameter to specify a default value. Esto hace que el argumento correspondiente sea opcional.That makes the corresponding argument optional.

Si se omite el argumento opcional en el sitio de llamada, se pasa el valor predeterminado a través de un temporal.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)
{
    // . . .
}

Comportamiento de alias en generalAliasing behavior in general

Al igual ref out que las variables y, in las variables son referencias o alias a ubicaciones existentes.Just like ref and out variables, in variables are references/aliases to existing locations.

Aunque no se permite que el destinatario escriba en ellos, leer un in parámetro puede observar valores diferentes como un efecto secundario de otras evaluaciones.While callee is not allowed to write into them, reading an in parameter can observe different values as a side effect of other evaluations.

Ejemplo: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 parámetros y captura de variables locales.in parameters and capturing of local variables.

Para el propósito de los parámetros de captura lambda/Async se in comporta igual que los out ref parámetros y.For the purpose of lambda/async capturing in parameters behave the same as out and ref parameters.

  • in los parámetros no se pueden capturar en un cierrein parameters cannot be captured in a closure
  • in no se permiten parámetros en los métodos de iteradorin parameters are not allowed in iterator methods
  • in no se permiten parámetros en métodos Asyncin parameters are not allowed in async methods

Variables temporales.Temporary variables.

Algunos usos del in paso de parámetros pueden requerir el uso indirecto de una variable local temporal:Some uses of in parameter passing may require indirect use of a temporary local variable:

  • in los argumentos siempre se pasan como alias directos cuando el sitio de la llamada usa in .in arguments are always passed as direct aliases when call-site uses in. No se usa nunca en este caso.Temporary is never used in such case.
  • in no es necesario que los argumentos sean alias directos cuando Call-site no usa in .in arguments are not required to be direct aliases when call-site does not use in. Si el argumento no es un valor l, se puede usar un temporal.When argument is not an LValue, a temporary may be used.
  • in el parámetro puede tener un valor predeterminado.in parameter may have default value. Cuando se omite el argumento correspondiente en el sitio de la llamada, el valor predeterminado se pasa a través de un temporal.When corresponding argument is omitted at the call site, the default value are passed via a temporary.
  • in los argumentos pueden tener conversiones implícitas, incluidas las que no conservan la identidad.in arguments may have implicit conversions, including those that do not preserve identity. En esos casos se usa un temporal.A temporary is used in those cases.
  • no se puede escribir en los receptores de llamadas de struct normales LValues (caso existente).receivers of ordinary struct calls may not be writeable LValues (existing case!). En esos casos se usa un temporal.A temporary is used in those cases.

La duración del argumento objetos temporales coincide con el ámbito más cercano del sitio de llamada.The life time of the argument temporaries matches the closest encompassing scope of the call-site.

La duración formal de las variables temporales es semánticamente significativa en escenarios que implican el análisis de escape de las variables devueltas por referencia.The formal life time of temporary variables is semantically significant in scenarios involving escape analysis of variables returned by reference.

Representación de metadatos de in parámetros.Metadata representation of in parameters.

Cuando System.Runtime.CompilerServices.IsReadOnlyAttribute se aplica a un parámetro ByRef, significa que el parámetro es un in parámetro.When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a byref parameter, it means that the parameter is an in parameter.

Además, si el método es abstracto o virtual, la firma de estos parámetros (y solo estos parámetros) debe tener 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].

Motivación: esto se hace para asegurarse de que, en un caso del método de invalidación o implementación de los in parámetros coinciden.Motivation: this is done to ensure that in a case of method overriding/implementing the in parameters match.

Los mismos requisitos se aplican a Invoke los métodos de los delegados.Same requirements apply to Invoke methods in delegates.

Motivación: esto es para asegurarse de que los compiladores existentes no se pueden omitir simplemente readonly al crear o asignar delegados.Motivation: this is to ensure that existing compilers cannot simply ignore readonly when creating or assigning delegates.

Devolución por referencia de solo lectura.Returning by readonly reference.

MotivaciónMotivation

La motivación de esta subcaracterística es aproximadamente simétrica a las razones de los in parámetros, evitando la copia, pero en el lado devuelto.The motivation for this sub-feature is roughly symmetrical to the reasons for the in parameters - avoiding copying, but on the returning side. Antes de esta característica, un método o un indexador tenían dos opciones: 1) devuelven por referencia y se exponen a posibles mutaciones o 2) devuelven por valor, lo que da como resultado la copia.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.

Solución ( ref readonly devuelve)Solution (ref readonly returns)

La característica permite que un miembro devuelva variables por referencia sin exponerlas a mutaciones.The feature allows a member to return variables by reference without exposing them to mutations.

Declarar ref readonly miembros que devuelvenDeclaring ref readonly returning members

Una combinación de modificadores ref readonly de la signatura de devolución se utiliza para indicar que el miembro devuelve una referencia de solo lectura.A combination of modifiers ref readonly on the return signature is used to to indicate that the member returns a readonly reference.

Para todos los propósitos, un ref readonly miembro se trata como una readonly variable, similar a readonly los campos y in parámetros.For all purposes a ref readonly member is treated as a readonly variable - similar to readonly fields and in parameters.

Por ejemplo ref readonly , los campos de miembro que tienen un tipo de estructura se clasifican de forma recursiva como readonly variables.For example fields of ref readonly member which has a struct type are all recursively classified as readonly variables. -Se permite pasarlos como in argumentos, pero no como ref out argumentos o.- 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 las devoluciones se permiten en los mismos lugares en los que ref se permiten las devoluciones.ref readonly returns are allowed in the same places were ref returns are allowed. Esto incluye indexadores, delegados, expresiones lambda y funciones locales.This includes indexers, delegates, lambdas, local functions.

  • No se permite sobrecargar las ref / ref readonly diferencias.It is not permitted to overload on ref/ref readonly / differences.

  • Se permite sobrecargar en valores de ByVal normales y ref readonly devolver diferencias.It is permitted to overload on ordinary byval and ref readonly return differences.

  • Para el propósito de OHI (sobrecargar, ocultar, implementar), ref readonly es similar pero distinto de ref .For the purpose of OHI (Overloading, Hiding, Implementing), ref readonly is similar but distinct from ref. Por ejemplo, un método que invalida ref readonly uno, debe ser y tener un tipo que se pueda ref readonly convertir en identidades.For example the a method that overrides ref readonly one, must itself be ref readonly and have identity-convertible type.

  • Para el propósito de las conversiones de grupo de delegado/lambda/método, ref readonly es similar pero distinto de ref .For the purpose of delegate/lambda/method group conversions, ref readonly is similar but distinct from ref. Las expresiones lambda y los candidatos de conversión de grupos de métodos aplicables tienen que coincidir ref readonly con la devolución del delegado de destino con ref readonly la devolución del tipo que se puede convertir en identidades.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.

  • Para el propósito de la varianza genérica, las ref readonly devoluciones son no variantes.For the purpose of generic variance, ref readonly returns are nonvariant.

Nota: no hay advertencias en las ref readonly devoluciones que tienen tipos de referencia o primitivos.NOTE: There are no warnings on ref readonly returns that have reference or primitives types. Puede que no sea puntual en general, pero en algunos casos el usuario debe/desea pasar primitivos como in .It may be pointless in general, but in some cases user must/want to pass primitives as in. Ejemplos: invalidar un método genérico como ref readonly T Method() cuando T se sustituyó por int .Examples - overriding a generic method like ref readonly T Method() when T was substituted to be int.

Es posible tener un analizador que advierte en casos de uso ineficaz de ref readonly las devoluciones, pero las reglas para dicho análisis serían demasiado aproximadas para formar parte de una especificación de lenguaje.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.

Devolver de ref readonly miembrosReturning from ref readonly members

Dentro del cuerpo del método, la sintaxis es la misma que con las devoluciones de referencia normales.Inside the method body the syntax is the same as with regular ref returns. Se readonly deduce del método contenedor.The readonly will be inferred from the containing method.

La motivación es que return ref readonly <expression> es una duración innecesaria y solo permite discrepancias en la readonly parte que siempre darían lugar a errores.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. refSin embargo, es necesario para la coherencia con otros escenarios en los que se pasa algo a través de un alias estricto frente a un valor.The ref is, however, required for consistency with other scenarios where something is passed via strict aliasing vs. by value.

A diferencia del caso con in parámetros, ref readonly devuelve nunca devolver a través de una copia local.Unlike the case with in parameters, ref readonly returns never return via a local copy. Es posible que la copia deje de existir inmediatamente después de que se devuelva este procedimiento.Considering that the copy would cease to exist immediately upon returning such practice would be pointless and dangerous. Por consiguiente, ref readonly las devoluciones son siempre referencias directas.Therefore ref readonly returns are always direct references.

Ejemplo: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];
    }
}

  • Un argumento de return ref debe ser un valor l (regla existente)An argument of return ref must be an LValue (existing rule)
  • Un argumento de return ref debe ser "Safe to Return" (regla existente)An argument of return ref must be "safe to return" (existing rule)
  • En un ref readonly miembro, return ref no es necesario que se pueda escribir en un argumento de.In a ref readonly member an argument of return ref is not required to be writeable . Por ejemplo, este miembro puede devolver una referencia a un campo de solo lectura o a uno de sus in parámetros.For example such member can ref-return a readonly field or one of its in parameters.

Safe para devolver reglas.Safe to Return rules.

La seguridad normal de las reglas de devoluciones para las referencias se aplicará también a las referencias de solo lectura.Normal safe to return rules for references will apply to readonly references as well.

Tenga en cuenta que ref readonly se puede obtener un a partir de un ref parámetro local/Parameter/Return normal, pero no al revés.Note that a ref readonly can be obtained from a regular ref local/parameter/return, but not the other way around. De lo contrario, la seguridad de las ref readonly devoluciones se deduce de la misma manera que para las ref devoluciones normales.Otherwise the safety of ref readonly returns is inferred the same way as for regular ref returns.

Teniendo en cuenta que RValues se puede pasar como parámetro y devolverse in como ref readonly se necesita una regla más: RValues no es seguro para devolver por referencia.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.

Tenga en cuenta la situación en la que se pasa un valor r a un in parámetro a través de una copia y después se devuelve en un formulario de 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. En el contexto del llamador, el resultado de dicha invocación es una referencia a los datos locales y, como tal, no es seguro devolver.In the context of the caller the result of such invocation is a reference to local data and as such is unsafe to return. Una vez que RValues no es seguro devolver, la regla existente #6 ya controla este caso.Once RValues are not safe to return, the existing rule #6 already handles this case.

Ejemplo: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;
}

Reglas actualizadas safe to return :Updated safe to return rules:

  1. las referencias a variables en el montón se pueden devolver de forma segurarefs to variables on the heap are safe to return
  2. los parámetros Ref/in se pueden devolver in de forma segura los parámetros de forma natural solo se pueden devolver como ReadOnly.ref/in parameters are safe to return in parameters naturally can only be returned as readonly.
  3. los parámetros out son seguros para devolver (pero se deben asignar definitivamente, ya que ya es el caso hoy)out parameters are safe to return (but must be definitely assigned, as is already the case today)
  4. los campos de struct de instancia se pueden devolver de forma segura siempre que el receptor sea seguro para devolverinstance struct fields are safe to return as long as the receiver is safe to return
  5. ' this ' no es seguro para devolver los miembros de struct'this' is not safe to return from struct members
  6. una referencia, devuelta desde otro método, es segura para devolver si todas las referencias que se pasan a ese método como parámetros formales eran seguras de devolver. En concreto, es irrelevante si el receptor es seguro devolver, independientemente de si el receptor es un struct, una clase o un tipo de parámetro de tipo genérico.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. No es seguro devolver RValues por referencia. Concretamente, RValues se pueden pasar como parámetros in.RValues are not safe to return by reference. Specifically RValues are safe to pass as in parameters.

Nota: existen otras reglas relacionadas con la seguridad de las devoluciones que entran en juego cuando se implican tipos de referencia y reasignaciones de referencia.NOTE: There are additional rules regarding safety of returns that come into play when ref-like types and ref-reassignments are involved. Las reglas se aplican igualmente a ref ref readonly los miembros y y, por lo tanto, no se mencionan aquí.The rules equally apply to ref and ref readonly members and therefore are not mentioned here.

Comportamiento de alias.Aliasing behavior.

ref readonly los miembros proporcionan el mismo comportamiento de alias que ref los miembros ordinarios (excepto para ser de solo lectura).ref readonly members provide the same aliasing behavior as ordinary ref members (except for being readonly). Por lo tanto, para la captura en lambdas, Async, iteradores, desbordamiento de pila, etc. se aplican las mismas restricciones.Therefore for the purpose of capturing in lambdas, async, iterators, stack spilling etc... the same restrictions apply. es decir,.- I.E. debido a la incapacidad de capturar las referencias reales y debido a la naturaleza de efectos secundarios de la evaluación de miembros, tales escenarios no se permiten.due to inability to capture the actual references and due to side-effecting nature of member evaluation such scenarios are disallowed.

Se permite y es necesario realizar una copia cuando el ref readonly valor devuelto es un receptor de métodos struct normales, que toman this como referencia ordinariamente modificable.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. Históricamente, en todos los casos en los que estas invocaciones se aplican a la variable de solo lectura, se realiza una copia local.Historically in all cases where such invocations are applied to readonly variable a local copy is made.

Representación de metadatos.Metadata representation.

Cuando System.Runtime.CompilerServices.IsReadOnlyAttribute se aplica a la devolución de un método que devuelve ByRef, significa que el método devuelve una referencia de solo lectura.When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to the return of a byref returning method, it means that the method returns a readonly reference.

Además, la firma de resultados de estos métodos (y solo esos métodos) debe tener modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute] .In addition, the result signature of such methods (and only those methods) must have modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute].

Motivación: esto es para asegurarse de que los compiladores existentes no se pueden omitir simplemente readonly al invocar métodos con ref readonly Returns.Motivation: this is to ensure that existing compilers cannot simply ignore readonly when invoking methods with ref readonly returns

Estructuras readonlyReadonly structs

En Resumen, es una característica que convierte el this parámetro de todos los miembros de instancia de un struct, excepto para los constructores, un in parámetro.In short - a feature that makes this parameter of all instance members of a struct, except for constructors, an in parameter.

MotivaciónMotivation

El compilador debe asumir que cualquier llamada al método en una instancia de struct puede modificar la instancia.Compiler must assume that any method call on a struct instance may modify the instance. En realidad, se pasa una referencia grabable al método como this parámetro y habilita completamente este comportamiento.Indeed a writeable reference is passed to the method as this parameter and fully enables this behavior. Para permitir tales invocaciones en readonly variables, las invocaciones se aplican a las copias temporales.To allow such invocations on readonly variables, the invocations are applied to temp copies. Esto podría ser poco intuitivo y a veces obliga a los usuarios a abandonar readonly por motivos de rendimiento.That could be unintuitive and sometimes forces people to abandon readonly for performance reasons.
Ejemplo: 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/

Después de agregar compatibilidad para in los parámetros y ref readonly devolver el problema de la copia defensiva se verá peor, ya que las variables de solo lectura volverán a ser más comunes.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.

SoluciónSolution

Permita readonly el modificador en las declaraciones de struct, lo que provocaría que this se tratase como in parámetro en todos los métodos de instancia de estructura, salvo para los constructores.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}";
    }
}

Restricciones en miembros de struct de solo lecturaRestrictions on members of readonly struct

  • Los campos de instancia de un struct de solo lectura deben ser de solo lectura.Instance fields of a readonly struct must be readonly.
    Motivación: solo se puede escribir externamente, pero no a través de miembros.Motivation: can only be written to externally, but not through members.
  • Las autopropiedades de instancia de un struct de solo lectura deben ser Get-only.Instance autoproperties of a readonly struct must be get-only.
    Motivación: consecuencia de la restricción en los campos de instancia.Motivation: consequence of restriction on instance fields.
  • La estructura ReadOnly no puede declarar eventos similares a campos.Readonly struct may not declare field-like events.
    Motivación: consecuencia de la restricción en los campos de instancia.Motivation: consequence of restriction on instance fields.

Representación de metadatos.Metadata representation.

Cuando System.Runtime.CompilerServices.IsReadOnlyAttribute se aplica a un tipo de valor, significa que el tipo es readonly struct .When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a value type, it means that the type is a readonly struct.

En concreto:In particular:

  • La identidad del IsReadOnlyAttribute tipo no es importante.The identity of the IsReadOnlyAttribute type is unimportant. De hecho, el compilador puede incrustarlo en el ensamblado contenedor si es necesario.In fact it can be embedded by the compiler in the containing assembly if needed.

ref/in métodos de extensiónref/in extension methods

En realidad, hay una propuesta existente ( https://github.com/dotnet/roslyn/issues/165) y el prototipo correspondiente () 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). Simplemente deseo confirmar que esta idea no es totalmente nueva.I just want to acknowledge that this idea is not entirely new. Sin embargo, es relevante aquí, ya que ref readonly quita de forma elegante el problema más polémico sobre tales métodos: Qué hacer con los receptores de valor r.It is, however, relevant here since ref readonly elegantly removes the most contentious issue about such methods - what to do with RValue receivers.

La idea general es permitir que los métodos de extensión tomen el this parámetro por referencia, siempre que se sepa que el tipo es un tipo de estructura.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
}

Los motivos para escribir estos métodos de extensión son principalmente:The reasons for writing such extension methods are primarily:

  1. Evitar la copia cuando el receptor es un struct grandeAvoid copying when receiver is a large struct
  2. Permitir métodos de extensión mutables en StructsAllow mutating extension methods on structs

Los motivos por los que no queremos permitir esto en las clasesThe reasons why we do not want to allow this on classes

  1. Tendría una finalidad muy limitada.It would be of very limited purpose.
  2. Interrumpiría la invariante de larga duración que una llamada al método no puede convertir null a no receptor para que se convierta después de la null invocación.It would break long standing invariant that a method call cannot turn non-null receiver to become null after invocation.

De hecho, actualmente no se puede establecer una variable que no sea null null a menos que sea explícitamente asignada o pasada por ref o out .In fact, currently a non-null variable cannot become null unless explicitly assigned or passed by ref or out. Esto ayuda en gran medida a la legibilidad u otras formas de análisis "puede ser un valor nulo aquí".That greatly aids readability or other forms of "can this be a null here" analysis. 3. Sería difícil conciliar con la semántica "evaluar una vez" de los accesos condicionales null.It would be hard to reconcile with "evaluate once" semantics of null-conditional accesses. Ejemplo: obj.stringField?.RefExtension(...) -es necesario capturar una copia de stringField para que la comprobación de valores NULL sea significativa, pero las asignaciones a this dentro de RefExtension no se reflejarán de nuevo en el campo.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.

Una capacidad para declarar métodos de extensión en Structs que toman el primer argumento por referencia era una solicitud de larga duración.An ability to declare extension methods on structs that take the first argument by reference was a long-standing request. Una de las consideraciones de bloqueo fue "¿Qué ocurre si el receptor no es un valor l?".One of the blocking consideration was "what happens if receiver is not an LValue?".

  • Existe un precedente que también se podría llamar a cualquier método de extensión como método estático (a veces es la única manera de resolver la ambigüedad).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). Esto indicaría que no se permiten los receptores de valor r.It would dictate that RValue receivers should be disallowed.
  • Por otro lado, existe la posibilidad de efectuar la invocación en una copia en situaciones similares cuando intervienen los métodos de instancia de struct.On the other hand there is a practice of making invocation on a copy in similar situations when struct instance methods are involved.

La razón por la que existe la "copia implícita" es que la mayoría de los métodos struct no modifican realmente el struct mientras no es capaz de indicarlo.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. Por lo tanto, la solución más práctica era simplemente hacer la invocación en una copia, pero esta práctica se conoce para perjudicar el rendimiento y provocar errores.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.

Ahora, con la disponibilidad de in los parámetros, es posible que una extensión señale el intento.Now, with availability of in parameters, it is possible for an extension to signal the intent. Por lo tanto, se puede resolver el dilema exigiendo que ref las extensiones se llamen con receptores que se pueden escribir, mientras que in las extensiones permiten la copia implícita si es necesario.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 extensiones y genéricos.in extensions and generics.

El propósito de ref los métodos de extensión es mutar el receptor directamente o invocar a los miembros mutantes.The purpose of ref extension methods is to mutate the receiver directly or by invoking mutating members. Por lo tanto, ref this T se permiten las extensiones mientras T esté restringida a una estructura.Therefore ref this T extensions are allowed as long as T is constrained to be a struct.

Por otra parte, in existen métodos de extensión específicamente para reducir la copia implícita.On the other hand in extension methods exist specifically to reduce implicit copying. Sin embargo, cualquier uso de un in T parámetro tendrá que realizarse a través de un miembro de interfaz.However any use of an in T parameter will have to be done through an interface member. Dado que todos los miembros de interfaz se consideran mutables, cualquier uso de este tipo requeriría una copia.Since all interface members are considered mutating, any such use would require a copy. -En lugar de reducir la copia, el efecto sería el contrario.- Instead of reducing copying, the effect would be the opposite. Por lo tanto, in this T no se permite cuando T es un parámetro de tipo genérico, independientemente de las restricciones.Therefore in this T is not allowed when T is a generic type parameter regardless of constraints.

Tipos válidos de métodos de extensión (Resumen):Valid kinds of extension methods (recap):

Ahora se permiten las siguientes formas de this declaración en un método de extensión:The following forms of this declaration in an extension method are now allowed:

  1. this T arg -extensión ByVal normal.this T arg - regular byval extension. (caso existente)(existing case)
  • T puede ser cualquier tipo, incluidos los tipos de referencia o los parámetros de tipo.T can be any type, including reference types or type parameters. La instancia será la misma variable después de la llamada.Instance will be the same variable after the call. Permite conversiones implícitas de este tipo de conversión de argumentos .Allows implicit conversions of this-argument-conversion kind. Se puede llamar a en RValues.Can be called on RValues.

  • in this T self - in Extension.in this T self - in extension. T debe ser un tipo de struct real.T must be an actual struct type. La instancia será la misma variable después de la llamada.Instance will be the same variable after the call. Permite conversiones implícitas de este tipo de conversión de argumentos .Allows implicit conversions of this-argument-conversion kind. Se puede llamar a en RValues (se puede invocar en un Temp si es necesario).Can be called on RValues (may be invoked on a temp if needed).

  • ref this T self - ref Extension.ref this T self - ref extension. T debe ser un tipo de estructura o un parámetro de tipo genérico restringido para ser un struct.T must be a struct type or a generic type parameter constrained to be a struct. La invocación puede escribir en la instancia.Instance may be written to by the invocation. Solo permite conversiones de identidad.Allows only identity conversions. Se debe llamar al método LValue grabable.Must be called on writeable LValue. (nunca se invoca a través de un Temp).(never invoked via a temp).

Variables locales de referencia de solo lectura.Readonly ref locals.

Razones.Motivation.

Una vez ref readonly que se introdujeron los miembros, era evidente que se debían emparejar con el tipo de local adecuado.Once ref readonly members were introduced, it was clear from the use that they need to be paired with appropriate kind of local. La evaluación de un miembro puede producir o observar efectos secundarios, por lo tanto, si el resultado se debe usar más de una vez, debe almacenarse.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. Las ref variables locales normales no ayudan aquí, ya que no se les puede asignar una readonly referencia.Ordinary ref locals do not help here since they cannot be assigned a readonly reference.

Solución.Solution.

Permitir declarar ref readonly variables locales.Allow declaring ref readonly locals. Se trata de un nuevo tipo de ref variables locales que no se escriben.This is a new kind of ref locals that is not writeable. Como consecuencia, las variables ref readonly locales pueden aceptar referencias a variables de solo lectura sin exponer estas variables a Escrituras.As a result ref readonly locals can accept references to readonly variables without exposing these variables to writes.

Declarar y usar ref readonly variables locales.Declaring and using ref readonly locals.

La sintaxis de tales variables locales usa ref readonly modificadores en el sitio de la declaración (en ese orden específico).The syntax of such locals uses ref readonly modifiers at declaration site (in that specific order). De forma similar a las ref variables locales normales, las ref readonly variables locales se deben inicializar con referencia a la declaración.Similarly to ordinary ref locals, ref readonly locals must be ref-initialized at declaration. A diferencia de las ref variables locales normales, las ref readonly variables locales pueden hacer referencia a readonly LValues como in parámetros, readonly campos y ref readonly métodos.Unlike regular ref locals, ref readonly locals can refer to readonly LValues like in parameters, readonly fields, ref readonly methods.

Para todos los propósitos ref readonly , un local se trata como una readonly variable.For all purposes a ref readonly local is treated as a readonly variable. La mayoría de las restricciones en el uso son las mismas que con readonly los campos o in parámetros.Most of the restrictions on the use are the same as with readonly fields or in parameters.

Por ejemplo, los campos de un in parámetro que tiene un tipo de estructura se clasifican de forma recursiva como readonly variables.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;
}

Restricciones en el uso de ref readonly variables localesRestrictions on use of ref readonly locals

Salvo por su readonly naturaleza, las ref readonly variables locales se comportan como ref variables locales ordinarias y están sujetas a las mismas restricciones.Except for their readonly nature, ref readonly locals behave like ordinary ref locals and are subject to exactly same restrictions.
Por ejemplo, las restricciones relacionadas con la captura en cierres, async la declaración en métodos o el safe-to-return análisis se aplican igualmente a ref readonly variables locales.For example restrictions related to capturing in closures, declaring in async methods or the safe-to-return analysis equally applies to ref readonly locals.

refExpresiones ternaria.Ternary ref expressions. (también conocido como "LValues condicional")(aka "Conditional LValues")

MotivaciónMotivation

El uso de ref y ref readonly variables locales expone una necesidad de inicializar las variables locales con una u otra variable de destino basada en una condición.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.

Una solución habitual es introducir un método como: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;
    }
}

Tenga en cuenta que Choice no es un reemplazo exacto de un ternario, ya que todos los argumentos deben evaluarse en el sitio de llamada, lo que ha provocado errores y comportamientos inintuitivos.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.

Lo siguiente no funcionará según lo esperado: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]);

SoluciónSolution

Permite un tipo especial de expresión condicional que se evalúa como una referencia a uno de los argumentos LValue basados en una condición.Allow special kind of conditional expression that evaluates to a reference to one of LValue argument based on a condition.

Usar una ref expresión ternaria.Using ref ternary expression.

La sintaxis para el ref tipo de una expresión condicional es <condition> ? ref <consequence> : ref <alternative>;The syntax for the ref flavor of a conditional expression is <condition> ? ref <consequence> : ref <alternative>;

Del mismo modo que con la expresión condicional ordinaria solo <consequence> o <alternative> se evalúa según el resultado de la expresión de condición booleana.Just like with the ordinary conditional expression only <consequence> or <alternative> is evaluated depending on result of the boolean condition expression.

A diferencia de la expresión condicional ordinaria, ref expresión condicional:Unlike ordinary conditional expression, ref conditional expression:

  • requiere que <consequence> y <alternative> sean LValues.requires that <consequence> and <alternative> are LValues.
  • ref la propia expresión condicional es un valor l yref conditional expression itself is an LValue and
  • ref la expresión condicional es grabable si tanto <consequence> como <alternative> son grabables LValuesref conditional expression is writeable if both <consequence> and <alternative> are writeable LValues

Ejemplos:Examples:
ref ternario es un valor l y, como tal, se puede pasar, asignar o devolver por referencia;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]);

Ser un valor l; también se puede asignar a.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;

Se puede usar como receptor de una llamada al método y omitir la copia si es necesario.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 ternario también se puede usar en un contexto normal (no 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]);

InconvenientesDrawbacks

Puedo ver dos argumentos principales contra la compatibilidad mejorada para referencias y referencias de solo lectura:I can see two major arguments against enhanced support for references and readonly references:

  1. Los problemas que se solucionan aquí son muy antiguos.The problems that are solved here are very old. ¿Por qué solucionarlos repentinamente ahora, especialmente porque no ayudaría el código existente?Why suddenly solve them now, especially since it would not help existing code?

Cuando encontramos C# y .net que se usan en nuevos dominios, algunos problemas se hacen más prominentes.As we find C# and .Net used in new domains, some problems become more prominent.
Como ejemplos de entornos que son más críticos que el promedio de las sobrecargas de cálculo, puedo MostrarAs examples of environments that are more critical than average about computation overheads, I can list

  • escenarios de Cloud/Datacenter en los que se factura el cálculo y la capacidad de respuesta es una ventaja competitiva.cloud/datacenter scenarios where computation is billed for and responsiveness is a competitive advantage.
  • Juegos/VR/AR con requisitos de tiempo real en latenciaGames/VR/AR with soft-realtime requirements on latencies

Esta característica no sacrifica ninguno de los puntos fuertes existentes, como la seguridad de tipos, a la vez que permite reducir las sobrecargas en algunos escenarios comunes.This feature does not sacrifice any of the existing strengths such as type-safety, while allowing to lower overheads in some common scenarios.

  1. ¿Podemos garantizar razonablemente que el destinatario de la llamada reproducirá las reglas cuando opte por los readonly contratos?Can we reasonably guarantee that the callee will play by the rules when it opts into readonly contracts?

Tenemos confianza similar cuando se usa out .We have similar trust when using out. Una implementación incorrecta de out puede provocar un comportamiento no especificado, pero en realidad suele ocurrir.Incorrect implementation of out can cause unspecified behavior, but in reality it rarely happens.

La realización de las reglas de comprobación formal que le resultan familiares es ref readonly mitigar aún más el problema de confianza.Making the formal verification rules familiar with ref readonly would further mitigate the trust issue.

AlternativasAlternatives

En realidad, el diseño de la competencia principal es "no hacer nada".The main competing design is really "do nothing".

Preguntas no resueltasUnresolved questions

Design MeetingsDesign 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