Referências somente leituraReadonly references

  • [x] proposta[x] Proposed
  • [x] protótipo[x] Prototype
  • [x] implementação: iniciada[x] Implementation: Started
  • [] Especificação: não iniciada[ ] Specification: Not Started

ResumoSummary

O recurso "referências somente leitura" é, na verdade, um grupo de recursos que aproveitam a eficiência da passagem de variáveis por referência, mas sem expor os dados às modificações: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 parâmetrosin parameters
  • Retornos de ref readonlyref readonly returns
  • readonly structsreadonly structs
  • ref/métodos de extensão inref/in extension methods
  • locais ref readonlyref readonly locals
  • ref expressões condicionaisref conditional expressions

Passando argumentos como referências somente leitura.Passing arguments as readonly references.

Há uma proposta existente que toca neste tópico https://github.com/dotnet/roslyn/issues/115 como um caso especial de parâmetros ReadOnly sem entrar em muitos detalhes.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. Aqui, quero apenas reconhecer que a ideia propriamente dita não é muito nova.Here I just want to acknowledge that the idea by itself is not very new.

MotivaçãoMotivation

Antes desse recurso C# não havia uma maneira eficiente de expressar um desejo de passar variáveis struct em chamadas de método para fins somente leitura sem intenção 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. O argumento regular por valor passando implica em copiar, o que adiciona custos desnecessários.Regular by-value argument passing implies copying, which adds unnecessary costs. Isso orienta os usuários a usar o argumento-ref passando e a contar com comentários/documentação para indicar que os dados não devem ser modificados pelo receptor.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. Não é uma boa solução por muitos motivos.It is not a good solution for many reasons.
Os exemplos são muitos operadores matemáticos/matrizes matemáticas em bibliotecas gráficas, como o XNA é conhecido por ter operandos de referência puramente devido a considerações de desempenho.The examples are numerous - vector/matrix math operators in graphics libraries like XNA are known to have ref operands purely because of performance considerations. Há código no próprio compilador Roslyn que usa structs para evitar alocações e as passa por referência para evitar a cópia de custos.There is code in Roslyn compiler itself that uses structs to avoid allocations and then passes them by reference to avoid copying costs.

Solução (parâmetros dein)Solution (in parameters)

Da mesma forma que os parâmetros de out, in parâmetros são passados como referências gerenciadas com garantias adicionais do receptor.Similarly to the out parameters, in parameters are passed as managed references with additional guarantees from the callee.
Ao contrário dos parâmetros de out que devem ser atribuídos pelo receptor antes de qualquer outro uso, in parâmetros não podem ser atribuídos pelo receptor.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 parâmetros permitem a eficácia da passagem de argumentos indiretos sem expor argumentos a mutações pelo receptor.As a result in parameters allow for effectiveness of indirect argument passing without exposing arguments to mutations by the callee.

Declarando parâmetros inDeclaring in parameters

parâmetros de in são declarados usando a palavra-chave in como um modificador na assinatura de parâmetro.in parameters are declared by using in keyword as a modifier in the parameter signature.

Para todas as finalidades, o parâmetro in é tratado como uma variável readonly.For all purposes the in parameter is treated as a readonly variable. A maioria das restrições sobre o uso de in parâmetros dentro do método são as mesmas que com readonly campos.Most of the restrictions on the use of in parameters inside the method are the same as with readonly fields.

Na verdade, um parâmetro in pode representar um campo de readonly.Indeed an in parameter may represent a readonly field. A similaridade de restrições não é um coincidência.Similarity of restrictions is not a coincidence.

Por exemplo, os campos de um parâmetro de in que tem um tipo struct são todos classificados recursivamente como variáveis 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 parâmetros são permitidos em qualquer lugar em que parâmetros de ByVal comuns são permitidos.in parameters are allowed anywhere where ordinary byval parameters are allowed. Isso inclui indexadores, operadores (incluindo conversões), delegados, lambdas, funções locais.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 não é permitido em combinação com out ou com qualquer coisa que out não combina com.in is not allowed in combination with out or with anything that out does not combine with.

  • Não é permitido sobrecarregar ref/out/diferenças de in.It is not permitted to overload on ref/out/in differences.

  • É permitido sobrecarregar em diferenças comuns de ByVal e in.It is permitted to overload on ordinary byval and in differences.

  • Com a finalidade de OHI (sobrecarregar, ocultar, implementar), in se comporta de forma semelhante a um parâmetro out.For the purpose of OHI (Overloading, Hiding, Implementing), in behaves similarly to an out parameter. Todas as mesmas regras se aplicam.All the same rules apply. Por exemplo, o método de substituição terá que Coincidir in parâmetros com in parâmetros de um tipo conversível de identidade.For example the overriding method will have to match in parameters with in parameters of an identity-convertible type.

  • Para a finalidade das conversões de grupo delegate/lambda/Method, in se comporta de forma semelhante a um parâmetro out.For the purpose of delegate/lambda/method group conversions, in behaves similarly to an out parameter. As lambdas e os candidatos à conversão do grupo de métodos aplicáveis terão que corresponder in parâmetros do delegado de destino com parâmetros in de um tipo de conversível de identidade.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 fins de variância genérica, in parâmetros são nonvariant.For the purpose of generic variance, in parameters are nonvariant.

Observação: não há avisos sobre in parâmetros que têm tipos de referência ou primitivos.NOTE: There are no warnings on in parameters that have reference or primitives types. Pode ser inútil em geral, mas, em alguns casos, o usuário deve/deseja passar primitivos como in.It may be pointless in general, but in some cases user must/want to pass primitives as in. Exemplos – substituindo um método genérico como Method(in T param) quando T foi substituído para ser intou ao ter 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)

É concebível ter um analisador que avisa em casos de uso ineficiente de parâmetros de in, mas as regras para essa análise seriam muito difusas para fazer parte de uma especificação de linguagem.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 em sites de chamada.Use of in at call sites. (argumentos dein)(in arguments)

Há duas maneiras de passar argumentos para in parâmetros.There are two ways to pass arguments to in parameters.

in argumentos podem corresponder in parâmetros:in arguments can match in parameters:

Um argumento com um modificador de in no site de chamada pode corresponder 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 argumento deve ser um LValue legível (*).in argument must be a readable LValue(*). Exemplo: M1(in 42) é inválidoExample: M1(in 42) is invalid

(*) A noção de LValue/rvalue varia entre os idiomas.(*) The notion of LValue/RValue vary between languages.
Aqui, por LValue, quero dizer uma expressão que representa um local que pode ser referenciado diretamente.Here, by LValue I mean an expression that represent a location that can be referred to directly. E RValue significa uma expressão que produz um resultado temporário que não persiste por conta própria.And RValue means an expression that yields a temporary result which does not persist on its own.

  • Em particular, é válido passar readonly campos, in parâmetros ou outras variáveis de readonly formalmente como argumentos de in.In particular it is valid to pass readonly fields, in parameters or other formally readonly variables as in arguments. Exemplo: dictionary[in Guid.Empty] é legal.Example: dictionary[in Guid.Empty] is legal. Guid.Empty é um campo somente leitura estático.Guid.Empty is a static readonly field.

  • in argumento deve ter o tipo Identity-conversível para o tipo do parâmetro.in argument must have type identity-convertible to the type of the parameter. Exemplo: M1<object>(in Guid.Empty) é inválido.Example: M1<object>(in Guid.Empty) is invalid. Guid.Empty não é conversível em identidade para objectGuid.Empty is not identity-convertible to object

A motivação para as regras acima é que in argumentos garantem a alias da variável de argumento.The motivation for the above rules is that in arguments guarantee aliasing of the argument variable. O receptor sempre recebe uma referência direta para o mesmo local, conforme representado pelo argumento.The callee always receives a direct reference to the same location as represented by the argument.

  • em raras situações em que os argumentos de in devem ser despejados em pilha devido a await expressões usadas como operandos da mesma chamada, o comportamento é o mesmo que os argumentos out e ref-se a variável não puder ser despejada de maneira referencialmente transparente, um erro será relatado.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.

Exemplos:Examples:

  1. M1(in staticField, await SomethingAsync()) é válido.M1(in staticField, await SomethingAsync()) is valid. staticField é um campo estático que pode ser acessado mais de uma vez sem efeitos colaterais observáveis.staticField is a static field which can be accessed more than once without observable side effects. Portanto, a ordem dos efeitos colaterais e os requisitos de alias podem ser fornecidos.Therefore both the order of side effects and aliasing requirements can be provided.
  2. M1(in RefReturningMethod(), await SomethingAsync()) produzirá um erro.M1(in RefReturningMethod(), await SomethingAsync()) will produce an error. RefReturningMethod() é um método ref retornando.RefReturningMethod() is a ref returning method. Uma chamada de método pode ter efeitos colaterais observáveis, portanto, ela deve ser avaliada antes do operando de SomethingAsync().A method call may have observable side effects, therefore it must be evaluated before the SomethingAsync() operand. No entanto, o resultado da invocação é uma referência que não pode ser preservada no ponto de suspensão await que torna impossível o requisito de referência direta.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.

Observação: os erros de despejo de pilha são considerados limitações específicas da implementação.NOTE: the stack spilling errors are considered to be implementation-specific limitations. Portanto, eles não têm efeito sobre a resolução de sobrecarga ou a inferência de lambda.Therefore they do not have effect on overload resolution or lambda inference.

Argumentos de ByVal comuns podem corresponder a parâmetros in:Ordinary byval arguments can match in parameters:

Argumentos regulares sem modificadores podem corresponder in parâmetros.Regular arguments without modifiers can match in parameters. Nesse caso, os argumentos têm as mesmas restrições relaxadas que os argumentos comuns de ByVal teriam.In such case the arguments have the same relaxed constraints as an ordinary byval arguments would have.

A motivação para esse cenário é que in parâmetros nas APIs podem resultar em inconveniências para o usuário quando argumentos não podem ser passados como referência direta-ex: literais, resultados computados ou await-Ed ou argumentos que ocorrem para ter tipos mais 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 esses casos têm uma solução trivial de armazenar o valor do argumento em um local temporário do tipo apropriado e passar esse local como um argumento 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.
Para reduzir a necessidade de tal compilador de código clichê pode executar a mesma transformação, se necessário, quando in modificador não está presente no site de chamada.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.

Além disso, em alguns casos, como invocação de operadores ou in métodos de extensão, não há nenhuma maneira sintática de especificar 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. Isso sozinho requer a especificação do comportamento de argumentos de ByVal comuns quando eles correspondem in parâmetros.That alone requires specifying the behavior of ordinary byval arguments when they match in parameters.

Em particular:In particular:

  • é válido passar RValues.it is valid to pass RValues. Uma referência a um temporário é passada nesse caso.A reference to a temporary is passed in such case. Exemplo:Example:
Print("hello");      // not an error.

void Print<T>(in T x)
{
  //. . .
}
  • conversões implícitas são permitidas.implicit conversions are allowed.

Isso é, na verdade, um caso especial para passar um RValueThis is actually a special case of passing an RValue

Uma referência a um valor temporário em retenção é transmitida nesse caso.A reference to a temporary holding converted value is passed in such case. Exemplo:Example:

Print<int>(Short.MaxValue)     // not an error.
  • em um caso de um receptor de um método de extensão in (em oposição aos métodos de extensão ref), os RValues ou as conversões implícitas desse argumento são permitidas.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. Uma referência a um valor temporário em retenção é transmitida nesse caso.A reference to a temporary holding converted value is passed in such case. Exemplo:Example:
public static IEnumerable<T> Concat<T>(in this (IEnumerable<T>, IEnumerable<T>) arg)  => . . .;

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

Mais informações sobre ref/métodos de extensão in são fornecidas mais adiante neste documento.More information on ref/in extension methods is provided further in this document.

  • o despejo do argumento devido a await operandos pode despejar "por valor", se necessário.argument spilling due to await operands could spill "by-value", if necessary. Em cenários em que não é possível fornecer uma referência direta ao argumento devido ao intervir await uma cópia do valor do argumento é despejada.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.
    Exemplo:Example:
M1(RefReturningMethod(), await SomethingAsync())   // not an error.

Como o resultado de uma invocação de efeito colateral é uma referência que não pode ser preservada em await suspensão, um temporário contendo o valor real será preservado (como faria em um caso de parâmetro ByVal comum).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 opcionais omitidosOmitted optional arguments

É permitido que um parâmetro in especifique um valor padrão.It is permitted for an in parameter to specify a default value. Isso torna o argumento correspondente opcional.That makes the corresponding argument optional.

Omitir o argumento opcional no site de chamada resulta na passagem do valor padrão por meio de um temporário.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)
{
    // . . .
}

Comportamento de alias em geralAliasing behavior in general

Assim como as variáveis ref e out, in variáveis são referências/aliases para os locais existentes.Just like ref and out variables, in variables are references/aliases to existing locations.

Embora o receptor não tenha permissão para gravar neles, a leitura de um parâmetro in pode observar valores diferentes como um efeito colateral de outras avaliações.While callee is not allowed to write into them, reading an in parameter can observe different values as a side effect of other evaluations.

Exemplo: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 e a captura de variáveis locais.in parameters and capturing of local variables.

Para a finalidade da captura de lambda/Async in parâmetros se comportam da mesma forma que os parâmetros out e ref.For the purpose of lambda/async capturing in parameters behave the same as out and ref parameters.

  • in parâmetros não podem ser capturados em um fechamentoin parameters cannot be captured in a closure
  • parâmetros de in não são permitidos em métodos iteradoresin parameters are not allowed in iterator methods
  • parâmetros de in não são permitidos em métodos assíncronosin parameters are not allowed in async methods

Variáveis temporárias.Temporary variables.

Alguns usos de in passagem de parâmetro podem exigir uso indireto de uma variável local temporária:Some uses of in parameter passing may require indirect use of a temporary local variable:

  • in argumentos são sempre passados como aliases diretos quando Call-site usa in.in arguments are always passed as direct aliases when call-site uses in. O temporário nunca é usado nesse caso.Temporary is never used in such case.
  • os argumentos de in não precisam ser aliases diretos quando o site de chamada não usa in.in arguments are not required to be direct aliases when call-site does not use in. Quando o argumento não é um LValue, um temporário pode ser usado.When argument is not an LValue, a temporary may be used.
  • in parâmetro pode ter um valor padrão.in parameter may have default value. Quando o argumento correspondente é omitido no site de chamada, o valor padrão é passado por meio de um temporário.When corresponding argument is omitted at the call site, the default value are passed via a temporary.
  • in argumentos podem ter conversões implícitas, incluindo aquelas que não preservam a identidade.in arguments may have implicit conversions, including those that do not preserve identity. Um temporário é usado nesses casos.A temporary is used in those cases.
  • receptores de chamadas struct comuns não podem ser LValue graváveis (caso existente! ).receivers of ordinary struct calls may not be writeable LValues (existing case!). Um temporário é usado nesses casos.A temporary is used in those cases.

O tempo de vida do argumento temporaries corresponde ao escopo de abrangência mais próximo do site de chamada.The life time of the argument temporaries matches the closest encompassing scope of the call-site.

O tempo de vida formal das variáveis temporárias é semanticamente significativo em cenários que envolvem a análise de escape de variáveis retornadas por referência.The formal life time of temporary variables is semantically significant in scenarios involving escape analysis of variables returned by reference.

Representação de metadados de parâmetros de in.Metadata representation of in parameters.

Quando System.Runtime.CompilerServices.IsReadOnlyAttribute é aplicado a um parâmetro ByRef, isso significa que o parâmetro é um parâmetro in.When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a byref parameter, it means that the parameter is an in parameter.

Além disso, se o método for abstract ou virtual, a assinatura desses parâmetros (e apenas esses parâmetros) deverá ter 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].

Motivação: isso é feito para garantir que, em um caso do método que substitui/implemente os parâmetros de in coincidam.Motivation: this is done to ensure that in a case of method overriding/implementing the in parameters match.

Os mesmos requisitos se aplicam aos métodos Invoke em delegados.Same requirements apply to Invoke methods in delegates.

Motivação: isso é para garantir que os compiladores existentes não possam simplesmente ignorar readonly ao criar ou atribuir delegados.Motivation: this is to ensure that existing compilers cannot simply ignore readonly when creating or assigning delegates.

Retornando por referência ReadOnly.Returning by readonly reference.

MotivaçãoMotivation

A motivação para esse subrecurso é aproximadamente simétrica para os motivos para os parâmetros de in – evitando a cópia, mas no lado de retorno.The motivation for this sub-feature is roughly symmetrical to the reasons for the in parameters - avoiding copying, but on the returning side. Antes desse recurso, um método ou um indexador tinha duas opções: 1) retornar por referência e ser exposto a possíveis mutações ou 2) retornar por valor, o que resulta em cópia.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.

Solução (ref readonly retorna)Solution (ref readonly returns)

O recurso permite que um membro retorne variáveis por referência sem expô-las a mutações.The feature allows a member to return variables by reference without exposing them to mutations.

Declarando ref readonly retornando MembrosDeclaring ref readonly returning members

Uma combinação de modificadores ref readonly na assinatura de retorno é usada para indicar que o membro retorna uma referência somente leitura.A combination of modifiers ref readonly on the return signature is used to to indicate that the member returns a readonly reference.

Para todas as finalidades, um membro de ref readonly é tratado como uma variável de readonly semelhante a campos de readonly e in parâmetros.For all purposes a ref readonly member is treated as a readonly variable - similar to readonly fields and in parameters.

Por exemplo, os campos de ref readonly membro que tem um tipo de struct são todos classificados recursivamente como variáveis de readonly.For example fields of ref readonly member which has a struct type are all recursively classified as readonly variables. -É permitido passá-los como in argumentos, mas não como ref ou out argumentos.- 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 retornos são permitidos nos mesmos locais ref retornos são permitidos.ref readonly returns are allowed in the same places were ref returns are allowed. Isso inclui indexadores, delegados, lambdas, funções locais.This includes indexers, delegates, lambdas, local functions.

  • Não é permitido sobrecarregar ref/ref readonly/diferenças.It is not permitted to overload on ref/ref readonly / differences.

  • É permitido sobrecarregar em diferenças comuns de retorno de ByVal e ref readonly.It is permitted to overload on ordinary byval and ref readonly return differences.

  • Com a finalidade de OHI (sobrecarregar, ocultar, implementar), ref readonly é semelhante, mas diferente de ref.For the purpose of OHI (Overloading, Hiding, Implementing), ref readonly is similar but distinct from ref. Por exemplo, o método que substitui ref readonly um, deve ser ref readonlydo e ter tipo de identidade conversível.For example the a method that overrides ref readonly one, must itself be ref readonly and have identity-convertible type.

  • Para a finalidade das conversões de grupo delegate/lambda/Method, ref readonly é semelhante, mas diferente de ref.For the purpose of delegate/lambda/method group conversions, ref readonly is similar but distinct from ref. As lambdas e os candidatos à conversão do grupo de métodos aplicáveis precisam corresponder ref readonly retorno do delegado de destino com ref readonly retorno do tipo que tem a identidade conversível.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 fins de variância genérica, ref readonly retornos são nonvariant.For the purpose of generic variance, ref readonly returns are nonvariant.

Observação: não há avisos sobre ref readonly retornos que têm tipos de referência ou primitivos.NOTE: There are no warnings on ref readonly returns that have reference or primitives types. Pode ser inútil em geral, mas, em alguns casos, o usuário deve/deseja passar primitivos como in.It may be pointless in general, but in some cases user must/want to pass primitives as in. Exemplos – substituindo um método genérico como ref readonly T Method() quando T foi substituído para ser int.Examples - overriding a generic method like ref readonly T Method() when T was substituted to be int.

É concebível ter um analisador que avisa em casos de uso ineficiente de ref readonly Devoluções, mas as regras para essa análise seriam muito difusas para fazer parte de uma especificação de linguagem.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.

Retornando de membros de ref readonlyReturning from ref readonly members

Dentro do corpo do método, a sintaxe é a mesma que com retornos de referência regulares.Inside the method body the syntax is the same as with regular ref returns. O readonly será inferido do método que o contém.The readonly will be inferred from the containing method.

A motivação é que return ref readonly <expression> é desnecessária e só permite incompatibilidades na parte readonly que sempre resultaria em erros.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. No entanto, o ref é necessário para a consistência com outros cenários em que algo é passado por meio de alias estrito vs. por valor.The ref is, however, required for consistency with other scenarios where something is passed via strict aliasing vs. by value.

Ao contrário do caso com in parâmetros, ref readonly retorna nunca retornar por meio de uma cópia local.Unlike the case with in parameters, ref readonly returns never return via a local copy. Considerar que a cópia deixaria de existir imediatamente ao retornar tal prática seria inútil e perigosa.Considering that the copy would cease to exist immediately upon returning such practice would be pointless and dangerous. Portanto ref readonly retorna são sempre referências diretas.Therefore ref readonly returns are always direct references.

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

  • Um argumento de return ref deve ser um LValue (regra existente)An argument of return ref must be an LValue (existing rule)
  • Um argumento de return ref deve ser "Safe para retornar" (regra existente)An argument of return ref must be "safe to return" (existing rule)
  • Em um membro ref readonly, um argumento de return ref não precisa ser gravável .In a ref readonly member an argument of return ref is not required to be writeable . Por exemplo, esse membro pode ser ref-retornar um campo ReadOnly ou um de seus parâmetros in.For example such member can ref-return a readonly field or one of its in parameters.

Seguro para retornar regras.Safe to Return rules.

A segurança normal para retornar regras para referências também será aplicada a referências ReadOnly.Normal safe to return rules for references will apply to readonly references as well.

Observe que um ref readonly pode ser obtido de um ref local/parâmetro/retorno regular, mas não o contrário.Note that a ref readonly can be obtained from a regular ref local/parameter/return, but not the other way around. Caso contrário, a segurança de retornos de ref readonly será inferida da mesma maneira que para retornos de ref regulares.Otherwise the safety of ref readonly returns is inferred the same way as for regular ref returns.

Considerando que os RValues podem ser passados como in parâmetro e retornados como ref readonly precisamos de mais uma regra- rvalues não são seguros para retornar por referência.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.

Considere a situação em que um RValue é passado para um parâmetro in por meio de uma cópia e retornada de volta em uma forma de um 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. No contexto do chamador, o resultado dessa invocação é uma referência a dados locais e, como tal, não é seguro retornar.In the context of the caller the result of such invocation is a reference to local data and as such is unsafe to return. Depois que RValues não são seguros de retornar, a regra existente #6 já lida com esse caso.Once RValues are not safe to return, the existing rule #6 already handles this case.

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

Regras de safe to return atualizadas:Updated safe to return rules:

  1. refs para variáveis no heap é seguro para retornarrefs to variables on the heap are safe to return
  2. os parâmetros REF/in são seguros para retornar parâmetros de in naturalmente só podem ser retornados como ReadOnly.ref/in parameters are safe to return in parameters naturally can only be returned as readonly.
  3. os parâmetros de saída são seguros para retornar (mas devem ser definitivamente atribuídos, como já é o caso hoje)out parameters are safe to return (but must be definitely assigned, as is already the case today)
  4. os campos de struct da instância são seguros para retornar, contanto que o receptor seja seguro para retornarinstance struct fields are safe to return as long as the receiver is safe to return
  5. ' this ' não é seguro para retornar de membros de struct'this' is not safe to return from struct members
  6. uma ref, retornada de outro método, é segura para retornar se todos os refs/esgotamentos passados para esse método como parâmetros formais eram seguros para retornar. especificamente é irrelevante se o destinatário é seguro de retornar, independentemente de o destinatário ser uma struct, uma classe ou um 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. Os rvalues não são seguros para retornar por referência. , especificamente, os rvalues são seguros como em parâmetros.RValues are not safe to return by reference. Specifically RValues are safe to pass as in parameters.

Observação: há regras adicionais sobre a segurança de retornos que entram em jogo quando tipos de referência e reatribuições de referência estão envolvidos.NOTE: There are additional rules regarding safety of returns that come into play when ref-like types and ref-reassignments are involved. As regras se aplicam igualmente aos membros ref e ref readonly e, portanto, não são mencionadas aqui.The rules equally apply to ref and ref readonly members and therefore are not mentioned here.

Comportamento de alias.Aliasing behavior.

ref readonly Membros fornecem o mesmo comportamento de alias que os membros comuns de ref (exceto para serem ReadOnly).ref readonly members provide the same aliasing behavior as ordinary ref members (except for being readonly). Portanto, para a finalidade de capturar em lambdas, Async, iteradores, despejo de pilha, etc... as mesmas restrições se aplicam.Therefore for the purpose of capturing in lambdas, async, iterators, stack spilling etc... the same restrictions apply. ,.- I.E. devido à incapacidade de capturar as referências reais e devido à natureza do efeito colateral da avaliação do membro, esses cenários não são permitidos.due to inability to capture the actual references and due to side-effecting nature of member evaluation such scenarios are disallowed.

É permitido e necessário fazer uma cópia quando ref readonly retorno é um destinatário de métodos struct regulares, que usam this como uma referência gravável comum.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. Historicamente, em todos os casos em que essas invocações são aplicadas à variável ReadOnly, é feita uma cópia local.Historically in all cases where such invocations are applied to readonly variable a local copy is made.

Representação de metadados.Metadata representation.

Quando System.Runtime.CompilerServices.IsReadOnlyAttribute é aplicado ao retorno de um método de retorno ByRef, isso significa que o método retorna uma referência ReadOnly.When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to the return of a byref returning method, it means that the method returns a readonly reference.

Além disso, a assinatura de resultado de tais métodos (e apenas esses métodos) deve ter modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute].In addition, the result signature of such methods (and only those methods) must have modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute].

Motivação: isso é para garantir que os compiladores existentes não possam simplesmente ignorar readonly ao invocar métodos com ref readonly retornaMotivation: this is to ensure that existing compilers cannot simply ignore readonly when invoking methods with ref readonly returns

Structs ReadOnlyReadonly structs

Em um recurso curto que faz this parâmetro de todos os membros de instância de um struct, exceto para construtores, um parâmetro in.In short - a feature that makes this parameter of all instance members of a struct, except for constructors, an in parameter.

MotivaçãoMotivation

O compilador deve assumir que qualquer chamada de método em uma instância de struct pode modificar a instância.Compiler must assume that any method call on a struct instance may modify the instance. Na verdade, uma referência gravável é passada para o método como this parâmetro e habilita totalmente esse comportamento.Indeed a writeable reference is passed to the method as this parameter and fully enables this behavior. Para permitir essas invocações em variáveis de readonly, as invocações são aplicadas a cópias temporárias.To allow such invocations on readonly variables, the invocations are applied to temp copies. Isso pode ser não intuitivo e, às vezes, força as pessoas a abandonar readonly por motivos de desempenho.That could be unintuitive and sometimes forces people to abandon readonly for performance reasons.
Exemplo: 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/

Depois de adicionar suporte para in parâmetros e ref readonly retorna o problema de cópia defensiva será pior, pois as variáveis ReadOnly se tornarão mais comuns.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.

SoluçãoSolution

Permitir readonly modificador em declarações de struct que resultaria na this tratada como parâmetro in em todos os métodos de instância de struct, exceto para construtores.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}";
    }
}

Restrições em membros de struct ReadOnlyRestrictions on members of readonly struct

  • Os campos de instância de uma struct ReadOnly devem ser ReadOnly.Instance fields of a readonly struct must be readonly.
    Motivação: só pode ser gravada externamente, mas não por meio de membros.Motivation: can only be written to externally, but not through members.
  • As propriedades autopropriedade de uma struct ReadOnly devem ser somente obtenção.Instance autoproperties of a readonly struct must be get-only.
    Motivação: conseqüência de restrição em campos de instância.Motivation: consequence of restriction on instance fields.
  • Struct ReadOnly não pode declarar eventos do tipo campo.Readonly struct may not declare field-like events.
    Motivação: conseqüência de restrição em campos de instância.Motivation: consequence of restriction on instance fields.

Representação de metadados.Metadata representation.

Quando System.Runtime.CompilerServices.IsReadOnlyAttribute é aplicado a um tipo de valor, isso significa que o tipo é um readonly struct.When System.Runtime.CompilerServices.IsReadOnlyAttribute is applied to a value type, it means that the type is a readonly struct.

Em particular:In particular:

  • A identidade do tipo de IsReadOnlyAttribute não é importante.The identity of the IsReadOnlyAttribute type is unimportant. Na verdade, ele pode ser inserido pelo compilador no assembly que o contém, se necessário.In fact it can be embedded by the compiler in the containing assembly if needed.

ref/métodos de extensão inref/in extension methods

Na verdade, há uma proposta existente (https://github.com/dotnet/roslyn/issues/165) e a PR (https://github.com/dotnet/roslyn/pull/15650)de protótipo correspondente.There is actually an existing proposal (https://github.com/dotnet/roslyn/issues/165) and corresponding prototype PR (https://github.com/dotnet/roslyn/pull/15650). Quero apenas reconhecer que essa ideia não é totalmente nova.I just want to acknowledge that this idea is not entirely new. No entanto, é relevante aqui, já que ref readonly remove elegantemente o problema mais contenciosos sobre tais métodos, o que fazer com os receptores de RValue.It is, however, relevant here since ref readonly elegantly removes the most contentious issue about such methods - what to do with RValue receivers.

A ideia geral é permitir que os métodos de extensão adotem o parâmetro this por referência, desde que o tipo seja conhecido como um tipo struct.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
}

Os motivos para escrever esses métodos de extensão são principalmente:The reasons for writing such extension methods are primarily:

  1. Evite copiar quando o destinatário é um struct grandeAvoid copying when receiver is a large struct
  2. Permitir a mutação de métodos de extensão em structsAllow mutating extension methods on structs

Os motivos pelos quais não queremos permitir isso em classesThe reasons why we do not want to allow this on classes

  1. Seria uma finalidade muito limitada.It would be of very limited purpose.
  2. Ela iria parar muito em constante distância que uma chamada de método não pode transformar o receptor nãonull para se tornar null após a invocação.It would break long standing invariant that a method call cannot turn non-null receiver to become null after invocation.

Na verdade, atualmente uma variável nãonull não pode se tornar null, a menos que seja explicitamente atribuída ou passada por ref ou out.In fact, currently a non-null variable cannot become null unless explicitly assigned or passed by ref or out. Isso ajuda muito a legibilidade ou outras formas da análise "pode ser uma nula aqui".That greatly aids readability or other forms of "can this be a null here" analysis. 3. Seria difícil reconciliar com a semântica "avaliar uma vez" de acessos condicionais nulos.It would be hard to reconcile with "evaluate once" semantics of null-conditional accesses. Exemplo: obj.stringField?.RefExtension(...)-é necessário capturar uma cópia de stringField para tornar a verificação nula significativa, mas as atribuições para this dentro de RefExtension não seriam refletidas de volta para o 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.

Uma capacidade de declarar métodos de extensão em structs que usam o primeiro argumento por referência era uma solicitação de longa duração.An ability to declare extension methods on structs that take the first argument by reference was a long-standing request. Uma das considerações de bloqueio foi "o que acontece se o destinatário não for um LValue?".One of the blocking consideration was "what happens if receiver is not an LValue?".

  • Há um precedente de que qualquer método de extensão também poderia ser chamado como um método estático (às vezes, é a única maneira de resolver a ambiguidade).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). Isso ditaria que os receptores de RValue não devem ser permitidos.It would dictate that RValue receivers should be disallowed.
  • Por outro lado, há uma prática de fazer a invocação em uma cópia em situações semelhantes quando os métodos de instância de struct estão envolvidos.On the other hand there is a practice of making invocation on a copy in similar situations when struct instance methods are involved.

O motivo pelo qual a "cópia implícita" existe é porque a maioria dos métodos struct não modifica realmente a estrutura, embora não seja capaz de indicar isso.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. Portanto, a solução mais prática era simplesmente fazer a invocação em uma cópia, mas essa prática é conhecida por prejudicar o desempenho e causar bugs.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.

Agora, com a disponibilidade de parâmetros de in, é possível que uma extensão sinalize a intenção.Now, with availability of in parameters, it is possible for an extension to signal the intent. Portanto, o enigma pode ser resolvido exigindo-se que as extensões de ref sejam chamadas com receptores graváveis, enquanto as extensões de in permitem a cópia implícita, se necessário.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();
}

extensões de in e genéricos.in extensions and generics.

A finalidade dos métodos de extensão de ref é mutar o receptor diretamente ou invocar os membros mutantes.The purpose of ref extension methods is to mutate the receiver directly or by invoking mutating members. Portanto, ref this T extensões são permitidas desde que T seja restrito a ser uma struct.Therefore ref this T extensions are allowed as long as T is constrained to be a struct.

Por outro lado in métodos de extensão existem especificamente para reduzir a cópia implícita.On the other hand in extension methods exist specifically to reduce implicit copying. No entanto, qualquer uso de um parâmetro de in T precisará ser feito por meio de um membro de interface.However any use of an in T parameter will have to be done through an interface member. Como todos os membros da interface são considerados mutação, qualquer uso exigiria uma cópia.Since all interface members are considered mutating, any such use would require a copy. -Em vez de reduzir a cópia, o efeito seria o oposto.- Instead of reducing copying, the effect would be the opposite. Portanto in this T não é permitido quando T é um parâmetro de tipo genérico, independentemente das restrições.Therefore in this T is not allowed when T is a generic type parameter regardless of constraints.

Tipos válidos de métodos de extensão (recapitulação):Valid kinds of extension methods (recap):

Agora, são permitidas as seguintes formas de declaração de this em um método de extensão:The following forms of this declaration in an extension method are now allowed:

  1. this T arg-extensão de ByVal regular.this T arg - regular byval extension. (caso existente)(existing case)
  • T pode ser qualquer tipo, incluindo tipos de referência ou parâmetros de tipo.T can be any type, including reference types or type parameters. A instância será a mesma variável após a chamada.Instance will be the same variable after the call. Permite conversões implícitas desse tipo de conversão de argumento .Allows implicit conversions of this-argument-conversion kind. Pode ser chamado em RValues.Can be called on RValues.

  • - in this T self``in extensão.in this T self - in extension. T deve ser um tipo struct real.T must be an actual struct type. A instância será a mesma variável após a chamada.Instance will be the same variable after the call. Permite conversões implícitas desse tipo de conversão de argumento .Allows implicit conversions of this-argument-conversion kind. Pode ser chamado em RValues (pode ser invocado em um temp, se necessário).Can be called on RValues (may be invoked on a temp if needed).

  • - ref this T self``ref extensão.ref this T self - ref extension. T deve ser um tipo struct ou um parâmetro de tipo genérico restrito a ser um struct.T must be a struct type or a generic type parameter constrained to be a struct. A instância pode ser gravada pela invocação.Instance may be written to by the invocation. Permite apenas conversões de identidade.Allows only identity conversions. Deve ser chamado em LValue gravável.Must be called on writeable LValue. (nunca é invocado por meio de uma Temp).(never invoked via a temp).

Locais de referência ReadOnly.Readonly ref locals.

Motivação.Motivation.

Uma vez que ref readonly Membros foram introduzidos, ele estava claro do uso de que precisam ser emparelhados com o tipo apropriado de local.Once ref readonly members were introduced, it was clear from the use that they need to be paired with appropriate kind of local. A avaliação de um membro pode produzir ou observar efeitos colaterais, portanto, se o resultado precisar ser usado mais de uma vez, ele precisará ser armazenado.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. Os locais ref comuns não ajudam aqui, pois não é possível atribuir uma referência readonly.Ordinary ref locals do not help here since they cannot be assigned a readonly reference.

Soluções.Solution.

Permitir a declaração de locais de ref readonly.Allow declaring ref readonly locals. Esse é um novo tipo de ref locais que não são graváveis.This is a new kind of ref locals that is not writeable. Como resultado ref readonly locais podem aceitar referências a variáveis ReadOnly sem expor essas variáveis a gravações.As a result ref readonly locals can accept references to readonly variables without exposing these variables to writes.

Declarando e usando ref readonly locais.Declaring and using ref readonly locals.

A sintaxe de tais locais usa ref readonly modificadores no site da declaração (nessa ordem específica).The syntax of such locals uses ref readonly modifiers at declaration site (in that specific order). Da mesma forma que os locais ref comuns, ref readonly locais devem ser inicializados como REF na declaração.Similarly to ordinary ref locals, ref readonly locals must be ref-initialized at declaration. Ao contrário de locais de ref regulares, ref readonly locais podem se referir a readonly LValues como parâmetros in, campos de readonly, métodos de ref readonly.Unlike regular ref locals, ref readonly locals can refer to readonly LValues like in parameters, readonly fields, ref readonly methods.

Para todas as finalidades, uma ref readonly local é tratada como uma variável readonly.For all purposes a ref readonly local is treated as a readonly variable. A maioria das restrições sobre o uso é a mesma de readonly campos ou parâmetros de in.Most of the restrictions on the use are the same as with readonly fields or in parameters.

Por exemplo, os campos de um parâmetro de in que tem um tipo struct são todos classificados recursivamente como variáveis 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;
}

Restrições no uso de locais de ref readonlyRestrictions on use of ref readonly locals

Exceto por sua natureza readonly, ref readonly locais se comportam como locais de ref comuns e estão sujeitos a exatamente as mesmas restrições.Except for their readonly nature, ref readonly locals behave like ordinary ref locals and are subject to exactly same restrictions.
Por exemplo, as restrições relacionadas à captura em fechamentos, declarando em async métodos ou na análise de safe-to-return se aplicam igualmente a ref readonly locais.For example restrictions related to capturing in closures, declaring in async methods or the safe-to-return analysis equally applies to ref readonly locals.

Expressões ternários ref.Ternary ref expressions. (também conhecido como "LValues condicionais")(aka "Conditional LValues")

MotivaçãoMotivation

O uso de ref e ref readonly locais expôs a necessidade de uma inicialização de referência desses locais com uma ou outra variável de destino com base em uma condição.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.

Uma solução alternativa típica é introduzir um 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;
    }
}

Observe que Choice não é uma substituição exata de um ternário, já que todos os argumentos devem ser avaliados no site de chamada, o que estava levando a um comportamento e a bugs não intuitivos.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.

O seguinte não funcionará conforme o 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]);

SoluçãoSolution

Permitir tipo especial de expressão condicional que é avaliada como uma referência a um dos argumentos LValue com base em uma condição.Allow special kind of conditional expression that evaluates to a reference to one of LValue argument based on a condition.

Usando ref expressão Ternário.Using ref ternary expression.

A sintaxe para o ref tipo de uma expressão condicional é <condition> ? ref <consequence> : ref <alternative>;The syntax for the ref flavor of a conditional expression is <condition> ? ref <consequence> : ref <alternative>;

Assim como com a expressão condicional comum somente <consequence> ou <alternative> é avaliada dependendo do resultado da expressão de condição booliana.Just like with the ordinary conditional expression only <consequence> or <alternative> is evaluated depending on result of the boolean condition expression.

Diferentemente da expressão condicional comum, ref expressão condicional:Unlike ordinary conditional expression, ref conditional expression:

  • requer que <consequence> e <alternative> sejam LValues.requires that <consequence> and <alternative> are LValues.
  • ref expressão condicional em si é um LValue eref conditional expression itself is an LValue and
  • ref expressão condicional é gravável se <consequence> e <alternative> são LValue graváveisref conditional expression is writeable if both <consequence> and <alternative> are writeable LValues

Exemplos:Examples:
ref ternário é um LValue e, como tal, pode ser passado/atribuído/retornado por referência;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]);

Sendo um LValue, ele também pode ser atribuído 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;

Pode ser usado como um receptor de uma chamada de método e ignorar a cópia, se necessário.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 ternário também pode ser usado em um contexto regular (não 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]);

DesvantagensDrawbacks

Posso ver dois argumentos principais em relação ao suporte aprimorado para referências e referências ReadOnly:I can see two major arguments against enhanced support for references and readonly references:

  1. Os problemas que são resolvidos aqui são muito antigos.The problems that are solved here are very old. Por que repentinamente solucioná-los agora, especialmente porque não ajudariam a usar o código existente?Why suddenly solve them now, especially since it would not help existing code?

Como encontramos C# e .net usados em novos domínios, alguns problemas se tornam mais proeminentes.As we find C# and .Net used in new domains, some problems become more prominent.
Como exemplos de ambientes que são mais críticos do que a média de sobrecargas de computação, posso listarAs examples of environments that are more critical than average about computation overheads, I can list

  • cenários de nuvem/datacenter em que a computação é cobrada e a capacidade de resposta é uma vantagem competitiva.cloud/datacenter scenarios where computation is billed for and responsiveness is a competitive advantage.
  • Jogos/VR/AR com requisitos de tempo real em latênciasGames/VR/AR with soft-realtime requirements on latencies

Esse recurso não sacrifica nenhum dos pontos fortes existentes, como a segurança de tipo, ao mesmo tempo que permite reduzir sobrecargas em alguns cenários comuns.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, razoavelmente, garantir que o receptor seja tocado pelas regras quando ele se deparar com contratos de readonly?Can we reasonably guarantee that the callee will play by the rules when it opts into readonly contracts?

Temos confiança semelhante ao usar out.We have similar trust when using out. A implementação incorreta de out pode causar comportamento não especificado, mas, na realidade, raramente acontece.Incorrect implementation of out can cause unspecified behavior, but in reality it rarely happens.

Fazer as regras de verificação formal familiarizadas com o ref readonly atenuaria ainda mais o problema de confiança.Making the formal verification rules familiar with ref readonly would further mitigate the trust issue.

AlternativasAlternatives

O design principal da concorrência é realmente "não fazer nada".The main competing design is really "do nothing".

Perguntas não resolvidasUnresolved questions

Criar reuniõesDesign 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