Membro parcial (Referência C#)
Um membro parcial tem uma declaração declarativa e, muitas vezes, uma declaração de execução. A declaração declarante não inclui um corpo. A declaração de execução indica o corpo do membro. Os membros parciais permitem que os designers de classe forneçam ganchos de membros que podem ser implementados por ferramentas como geradores de código-fonte. Tipos e membros parciais fornecem uma maneira para desenvolvedores humanos escreverem parte de um tipo, enquanto as ferramentas escrevem outras partes do tipo. Se o desenvolvedor não fornecer uma declaração de implementação opcional, o compilador poderá remover a declaração declarando em tempo de compilação. Aos membros parciais aplicam-se as seguintes condições:
- As declarações devem começar com a palavra-chave contextual parcial.
- As assinaturas em ambas as partes do tipo parcial devem corresponder.
A partial
palavra-chave não é permitida em construtores, finalizadores, operadores sobrecarregados ou declarações de eventos. Antes do C# 13, partial
não era permitido em propriedades ou indexadores.
Um método parcial não é necessário para ter uma declaração de implementação nos seguintes casos:
- Ele não tem nenhum modificador de acessibilidade (incluindo o padrão
private
). - Ele retorna
void
. - Não tem parâmetros
out
. - Ele não tem nenhum dos seguintes modificadores
virtual
, ,override
,sealed
new
, ouextern
.
Qualquer membro que não esteja em conformidade com todas essas restrições (por exemplo, public virtual partial void
método), deve fornecer uma implementação. Propriedades parciais e indexadores devem ter uma implementação.
O exemplo a seguir mostra um método parcial que está em conformidade com as restrições anteriores:
partial class MyPartialClass
{
// Declaring definition
partial void OnSomethingHappened(string s);
}
// This part can be in a separate file.
partial class MyPartialClass
{
// Comment out this method and the program
// will still compile.
partial void OnSomethingHappened(string s) =>
Console.WriteLine($"Something happened: {s}");
}
Membros parciais também podem ser úteis em combinação com geradores de origem. Por exemplo, um regex pode ser definido usando o seguinte padrão:
public partial class RegExSourceGenerator
{
[GeneratedRegex("cat|dog", RegexOptions.IgnoreCase, "en-US")]
private static partial Regex CatOrDogGeneratedRegex();
private static void EvaluateText(string text)
{
if (CatOrDogGeneratedRegex().IsMatch(text))
{
// Take action with matching text
}
}
}
O exemplo anterior mostra um método parcial que deve ter uma declaração de implementação. Como parte de uma compilação, o gerador de origem de expressão regular cria a declaração de implementação.
O exemplo a seguir mostra uma declaração declarante e uma declaração de implementação para uma classe. Como o tipo de retorno do método não void
é (é string
) e seu acesso é public
, o método deve ter uma declaração de implementação:
// Declaring declaration
public partial class PartialExamples
{
/// <summary>
/// Gets or sets the number of elements that the List can contain.
/// </summary>
public partial int Capacity { get; set; }
/// <summary>
/// Gets or sets the element at the specified index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The string stored at that index</returns>
public partial string this[int index] { get; set; }
public partial string? TryGetAt(int index);
}
public partial class PartialExamples
{
private List<string> _items = [
"one",
"two",
"three",
"four",
"five"
];
// Implementing declaration
/// <summary>
/// Gets or sets the number of elements that the List can contain.
/// </summary>
/// <remarks>
/// If the value is less than the current capacity, the list will shrink to the
/// new value. If the value is negative, the list isn't modified.
/// </remarks>
public partial int Capacity
{
get => _items.Count;
set
{
if ((value != _items.Count) && (value >= 0))
{
_items.Capacity = value;
}
}
}
public partial string this[int index]
{
get => _items[index];
set => _items[index] = value;
}
/// <summary>
/// Gets the element at the specified index.
/// </summary>
/// <param name="index">The index</param>
/// <returns>The string stored at that index, or null if out of bounds</returns>
public partial string? TryGetAt(int index)
{
if (index < _items.Count)
{
return _items[index];
}
return null;
}
}
O exemplo anterior ilustra as regras sobre como as duas declarações são combinadas:
- Correspondências de assinaturas: Em geral, as assinaturas para as declarações declarativas e de implementação devem corresponder. Isso inclui modificador de acessibilidade em métodos, propriedades, indexadores e acessadores individuais. Inclui o tipo de parâmetro e modificadores ref-kind em todos os parâmetros. O tipo de retorno e qualquer modificador ref-kind devem corresponder. Os nomes dos membros da Tuple devem corresponder. No entanto, algumas regras são flexíveis:
- As declarações de declaração e implementação podem ter diferentes configurações de anotações anuláveis . Ou seja, um pode ser anulável alheio e o outro anulável habilitado.
- As diferenças de anulabilidade que não envolvem anulabilidade alheia geram um aviso.
- Os valores dos parâmetros padrão não precisam corresponder. O compilador emite um aviso se a declaração de implementação de um método ou indexador declara um valor de parâmetro padrão.
- O compilador emite um aviso quando os nomes dos parâmetros não correspondem. O IL emitido contém os nomes dos parâmetros da declaração declarante.
- Comentários da documentação: Os comentários da documentação podem ser incluídos a partir de qualquer declaração. Se tanto a declaração como a declaração de execução incluírem observações documentais, as observações da declaração de execução são incluídas. No exemplo anterior, os comentários da documentação incluem:
- Para a
Capacity
propriedade, os comentários são retirados da declaração de execução. As observações da declaração de execução são utilizadas quando ambas as declarações contêm///
observações. - Para o indexador, os comentários são retirados da declaração declarante. A declaração de execução não inclui quaisquer
///
comentários. - Para
TryGetAt
, as observações são retiradas da declaração de execução. A declaração declarativa não inclui comentários///
. - O XML gerado tem comentários de documentação para todos os
public
membros.
- Para a
- A maioria das declarações de atributo são combinadas. No entanto, todos os atributos de informações do chamador são definidos com
AllowMultiple=false
. O compilador reconhece qualquer atributo de informação do chamador na declaração de declaração. Todos os atributos de informações do chamador na declaração de implementação são ignorados. O compilador emite um aviso se você adicionar atributos de informações do chamador na declaração de implementação.