Membros da instância readonly
Observação
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ela inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas divergências entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da reunião de design de idioma (LDM).
Você pode saber mais sobre o processo de adoção de speclets de recursos no padrão de linguagem C# no artigo sobre as especificações de .
Problema do especialista: <https://github.com/dotnet/csharplang/issues/1710>
Resumo
Forneça uma maneira de especificar que membros de instância individuais em um struct não modifiquem o estado, da mesma forma que readonly struct
especifica que nenhum membro de instância modifica o estado.
Vale observar que o membro da instância readonly != membro da instância pura. Um membro da instância pura garante que nenhum estado será modificado. Um membro de instância readonly
garante apenas que o estado da instância não será modificado.
Todos os membros da instância em um readonly struct
podem ser considerados implicitamente membros da instância readonly Membros da instância readonly explícitos declarados em structs não readonly se comportariam da mesma maneira. Por exemplo, eles ainda criariam cópias ocultas se você chamasse um membro da instância (na instância atual ou em um campo da instância) que fosse não readonly.
Motivação
Antes do C# 8.0, os usuários podiam criar tipos readonly struct
, que o compilador impõe que todos os campos sejam readonly (e, por extensão, que nenhum membro da instância modifique o estado). No entanto, há alguns cenários em uma API existente expõe campos acessíveis ou tem uma combinação de membros mutados e não mutados. Nessas circunstâncias, não é possível marcar o tipo como readonly
(seria uma alteração interruptiva).
Geralmente, isso não causa muito impacto, exceto no caso de parâmetros de in
. Com parâmetros in
para structs não readonly, o compilador fará uma cópia do parâmetro para cada invocação de membro da instância, pois não pode garantir que a invocação não modificará o estado interno. Isso pode resultar em muitas cópias e em um desempenho geral pior do que se você tivesse transmitido a estrutura diretamente por valor. Para um exemplo, veja este código no sharplab
Alguns outros cenários em que cópias ocultas podem ocorrer incluem static readonly
campos e literais. Se tiverem suporte no futuro, as constantes blittable ficarão na mesma situação; atualmente, todas precisam de uma cópia completa (na invocação do membro da instância) quando o struct não está marcado readonly
.
Design
Permitir que um usuário especifique que um membro de instância é, por si só, readonly
e não modifica o estado da instância (com toda a verificação apropriada feita pelo compilador, é claro). Por exemplo:
public struct Vector2
{
public float x;
public float y;
public readonly float GetLengthReadonly()
{
return MathF.Sqrt(LengthSquared);
}
public float GetLength()
{
return MathF.Sqrt(LengthSquared);
}
public readonly float GetLengthIllegal()
{
var tmp = MathF.Sqrt(LengthSquared);
x = tmp; // Compiler error, cannot write x
y = tmp; // Compiler error, cannot write y
return tmp;
}
public readonly float LengthSquared
{
get
{
return (x * x) +
(y * y);
}
}
}
public static class MyClass
{
public static float ExistingBehavior(in Vector2 vector)
{
// This code causes a hidden copy, the compiler effectively emits:
// var tmpVector = vector;
// return tmpVector.GetLength();
//
// This is done because the compiler doesn't know that `GetLength()`
// won't mutate `vector`.
return vector.GetLength();
}
public static float ReadonlyBehavior(in Vector2 vector)
{
// This code is emitted exactly as listed. There are no hidden
// copies as the `readonly` modifier indicates that the method
// won't mutate `vector`.
return vector.GetLengthReadonly();
}
}
As instâncias readonly podem ser aplicadas a acessadores de propriedade para indicar que this
não será alterado no acessador. Os exemplos a seguir têm setters readonly porque esses acessadores modificam o estado do campo membro, mas não o valor desse campo membro.
public readonly int Prop1
{
get
{
return this._store["Prop1"];
}
set
{
this._store["Prop1"] = value;
}
}
Quando readonly
é aplicado à sintaxe da propriedade, significa que todos os acessadores são readonly
.
public readonly int Prop2
{
get
{
return this._store["Prop2"];
}
set
{
this._store["Prop2"] = value;
}
}
Só é possível aplicar readonly a acessadores que não modificam o tipo de contenção.
public int Prop3
{
readonly get
{
return this._prop3;
}
set
{
this._prop3 = value;
}
}
Somente leitura pode ser aplicado a algumas propriedades implementadas automaticamente, mas não terá um efeito considerável. O compilador tratará todos os getters implementados automaticamente como somente leitura se a palavra-chave readonly
estiver presente ou não.
// Allowed
public readonly int Prop4 { get; }
public int Prop5 { readonly get; set; }
// Not allowed
public int Prop6 { readonly get; }
public readonly int Prop7 { get; set; }
public int Prop8 { get; readonly set; }
Somente leitura pode ser aplicado a eventos implementados manualmente, mas não a eventos semelhantes a um campo. Não é possível aplicar readonly a acessadores de eventos individuais (adicionar/remover).
// Allowed
public readonly event Action<EventArgs> Event1
{
add { }
remove { }
}
// Not allowed
public readonly event Action<EventArgs> Event2;
public event Action<EventArgs> Event3
{
readonly add { }
readonly remove { }
}
public static readonly event Event4
{
add { }
remove { }
}
Alguns outros exemplos de sintaxe:
- Membros aptos para expressão:
public readonly float ExpressionBodiedMember => (x * x) + (y * y);
- Restrições genéricas:
public readonly void GenericMethod<T>(T value) where T : struct { }
O compilador emitiria o membro da instância, como de costume, e também emitiria um atributo reconhecido pelo compilador indicando que o membro da instância não modifica o estado. Isso faz com que o parâmetro this
oculto se torne in T
em vez de ref T
.
Isso permitiria que o usuário chamasse esse método de instância com segurança sem que o compilador precisasse fazer uma cópia.
As restrições incluem:
- O modificador
readonly
não pode ser aplicado a métodos estáticos, construtores ou destruidores. - O modificador
readonly
não pode ser aplicado aos delegados. - O modificador
readonly
não pode ser aplicado a membros de classe ou interface.
Desvantagens
As mesmas desvantagens que existem com os métodos readonly struct
atualmente. Certos códigos ainda podem causar cópias ocultas.
Observações
O uso de um atributo ou outra palavra-chave talvez seja possível também.
Esta proposta é relacionada a (mas é mais um subconjunto de) functional purity
e/ou constant expressions
, ambos com algumas propostas existentes.
C# feature specifications