Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas discrepâ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 linguagem (LDM).
Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .
Edição campeã: https://github.com/dotnet/csharplang/issues/9058
Resumo
Permita que o partial
modificador em eventos e construtores separe partes de declaração e implementação, semelhante a métodos parciais e propriedades/indexadores parciais.
partial class C
{
partial C(int x, string y);
partial event Action<int, string> MyEvent;
}
partial class C
{
partial C(int x, string y) { }
partial event Action<int, string> MyEvent
{
add { }
remove { }
}
}
Motivação
O C# já oferece suporte a métodos, propriedades e indexadores parciais. Eventos parciais e construtores estão faltando.
Eventos parciais seriam úteis para bibliotecas de eventos fracas , onde o usuário poderia escrever definições:
partial class C
{
[WeakEvent]
partial event Action<int, string> MyEvent;
void M()
{
RaiseMyEvent(0, "a");
}
}
E um gerador de código-fonte fornecido pela biblioteca forneceria implementações:
partial class C
{
private readonly WeakEvent _myEvent;
partial event Action<int, string> MyEvent
{
add { _myEvent.Add(value); }
remove { _myEvent.Remove(value); }
}
protected void RaiseMyEvent(int x, string y)
{
_myEvent.Invoke(x, y);
}
}
Eventos parciais e construtores parciais também seriam úteis para gerar código de interoperabilidade, como no Xamarin , onde o usuário poderia escrever definições parciais de construtor e evento:
partial class AVAudioCompressedBuffer : AVAudioBuffer
{
[Export("initWithFormat:packetCapacity:")]
public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity);
[Export("create:")]
public partial event EventHandler Created;
}
E o gerador de origem geraria as ligações (para Objective-C neste caso):
partial class AVAudioCompressedBuffer : AVAudioBuffer
{
[BindingImpl(BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity) : base(NSObjectFlag.Empty)
{
// Call Objective-C runtime:
InitializeHandle(
global::ObjCRuntime.NativeHandle_objc_msgSendSuper_NativeHandle_UInt32(
this.SuperHandle,
Selector.GetHandle("initWithFormat:packetCapacity:"),
format.GetNonNullHandle(nameof(format)),
packetCapacity),
"initWithFormat:packetCapacity:");
}
public partial event EventHandler Created
{
add { /* ... */ }
remove { /* ... */ }
}
}
Design Detalhado
Geral
A sintaxe da declaração de evento (§15.8.1) é ampliada para permitir o modificador partial
.
event_declaration
- : attributes? event_modifier* 'event' type variable_declarators ';'
+ : attributes? event_modifier* 'partial'? 'event' type variable_declarators ';'
- | attributes? event_modifier* 'event' type member_name
+ | attributes? event_modifier* 'partial'? 'event' type member_name
'{' event_accessor_declarations '}'
;
A sintaxe da declaração do construtor da instância (§15.11.1) é ampliada para permitir o uso do partial
modificador.
constructor_declaration
- : attributes? constructor_modifier* constructor_declarator constructor_body
+ : attributes? constructor_modifier* 'partial'? constructor_declarator constructor_body
;
Observe que há uma proposta para permitir o partial
modificador em qualquer lugar entre os modificadores, em vez de apenas como o último (também para declarações de método, propriedade e tipo).
Uma declaração de evento com o partial
modificador é considerada uma declaração de evento parcial e está associada a um ou mais eventos parciais com os nomes especificados (observe que uma declaração de evento sem acessadores pode definir vários eventos).
Uma declaração de construtor com o partial
modificador é dito ser uma declaração de construtor parcial e está associada a um construtor parcial com a assinatura especificada.
Diz-se que uma declaração de evento parcial é uma declaração de implementação quando especifica o modificador event_accessor_declarations
ou possui o modificador extern
.
Caso contrário, trata-se de uma declaração definidora.
Diz-se que uma declaração parcial do construtor é uma declaração definidora quando tem um corpo de ponto-e-vírgula e não tem o extern
modificador.
Caso contrário, trata-se de uma declaração de execução .
partial class C
{
// defining declarations
partial C();
partial C(int x);
partial event Action E, F;
// implementing declarations
partial C() { }
partial C(int x) { }
partial event Action E { add { } remove { } }
partial event Action F { add { } remove { } }
}
Apenas a declaração definidora de um membro parcial participa do processo de pesquisa e é considerada nos locais de uso e para a emissão dos metadados. (Exceto para comentários de documentação, conforme detalhado abaixo.) A assinatura da declaração de implementação é utilizada para a análise de nulidade dos corpos associados.
Um evento parcial ou construtor:
- Pode ser declarado apenas como membro de uma classe parcial.
- Deve ter uma declaração definidora e uma declaração de execução.
- Não é permitido ter o modificador
abstract
. - Não é possível implementar explicitamente um membro da interface.
Um evento parcial não é semelhante a um campo (§15.8.2), ou seja:
- Ele não tem nenhum armazenamento de backup ou acessadores gerados pelo compilador.
- Ele só pode ser usado em
+=
e-=
operações, não como um valor.
Uma declaração de construtor parcial definidora não pode ter um inicializador de construtor (: this()
ou : base()
; §15.11.2).
Pausa de análise
Permitir o modificador partial
em mais contextos é uma alteração de rutura.
class C
{
partial F() => new partial(); // previously a method, now a constructor
@partial F() => new partial(); // workaround to keep it a method
}
class partial { }
Para simplificar o analisador de linguagem, o modificador partial
é aceito em qualquer declaração semelhante a método (ou seja, em funções locais e métodos de script de nível superior também), mesmo que não especifiquemos explicitamente as alterações gramaticais.
Atributos
Os atributos do evento ou construtor resultante são os atributos combinados das declarações parciais nas posições correspondentes. Os atributos combinados são concatenados em uma ordem não especificada e as duplicatas não são removidas.
O method
attribute_target (§22.3) é ignorado em declarações parciais de eventos.
Os atributos de acessório são usados apenas em declarações de acessório, que só podem estar presentes sob a declaração de implementação.
Observe que param
e return
attribute_targets já são ignorados em todas as declarações de evento.
Os atributos Caller-info na declaração de implementação são ignorados pelo compilador, conforme especificado pela proposta de propriedades parciais na seção Atributos Caller-info (observe que ele se aplica a todos os membros parciais, o que inclui eventos parciais e construtores).
Assinaturas
Ambas as declarações de um membro parcial devem ter assinaturas correspondentes semelhantes às propriedades parciais:
- As diferenças de tipo e tipo de referência entre declarações parciais, significativas para o tempo de execução, resultam num erro de compilação.
- Diferenças nos nomes dos elementos de tupla entre declarações parciais resultam em um erro em tempo de compilação.
- As declarações devem ter os mesmos modificadores, embora os modificadores possam aparecer em uma ordem diferente.
- Exceção: não se aplica ao
extern
modificador que só pode figurar na declaração de execução.
- Exceção: não se aplica ao
- Todas as outras diferenças sintáticas nas assinaturas de declarações parciais resultam em um aviso em tempo de compilação, com as seguintes exceções:
- As listas de atributos não precisam corresponder conforme descrito acima.
- Diferenças de contexto anuláveis (como ignorado versus anotado) não geram avisos.
- Os valores de parâmetro padrão não precisam corresponder, mas um aviso é relatado quando a declaração do construtor de implementação tem valores de parâmetro padrão (porque eles seriam ignorados, já que apenas a declaração definidora participa da pesquisa).
- Um aviso ocorre quando os nomes dos parâmetros diferem entre a definição e a implementação de declarações do construtor.
- As diferenças de anulabilidade que não envolvem anulabilidade implícita resultam em advertências.
Comentários da documentação
É permitido incluir comentários em documentos tanto sobre a declaração definidora como sobre a declaração de execução. Observe que os comentários doc não são suportados em acessadores de eventos.
Quando os comentários doc estão presentes em apenas uma das declarações de um membro parcial, esses comentários doc são usados normalmente (apresentados através de APIs Roslyn, emitidos para o arquivo XML da documentação).
Quando os comentários doc estão presentes em ambas as declarações de um membro parcial, todos os comentários doc sobre a declaração definidora são descartados, e apenas os comentários doc sobre a declaração de execução são usados.
Quando os nomes dos parâmetros diferem entre declarações de um membro parcial, os elementos paramref
usam os nomes dos parâmetros da declaração que está associada ao comentário de documentação no código-fonte.
Por exemplo, um comentário de documentação paramref
colocado em uma declaração de implementação refere-se aos símbolos de parâmetro da declaração de implementação usando seus nomes de parâmetro.
Isso pode ser confuso, porque a assinatura de metadados usará nomes de parâmetros da declaração definidora.
Recomenda-se assegurar que os nomes dos parâmetros sejam consistentes entre as declarações de um membro parcial para evitar essa confusão.
Perguntas abertas
Tipos de membros
Queremos eventos parciais, construtores, operadores, campos? Propomos os dois primeiros tipos de membros, mas qualquer outro subconjunto poderia ser considerado.
Construtores primários parciais também podem ser considerados, por exemplo, permitindo que o usuário tenha a mesma lista de parâmetros em várias declarações de tipo parcial.
Locais de atributos
Devemos reconhecer o especificador de destino de [method:]
atributo para eventos parciais (ou apenas as declarações definidoras)?
Em seguida, os atributos de acessador resultantes seriam a concatenação de atributos -targeting de ambas (ou apenas as partes definidoras) da declaração, além de method
atributos de autodirecionamento e method
-targeting dos acessadores da declaração de implementação.
A combinação de atributos de diferentes tipos de declaração seria sem precedentes e, de fato, a implementação atual da correspondência de atributos em Roslyn não suporta isso.
Também podemos considerar reconhecer [param:]
e [return:]
, não apenas em eventos parciais, mas em todos os eventos do tipo campo e externos.
Para obter mais detalhes, veja https://github.com/dotnet/roslyn/issues/77254.
C# feature specifications