Membro parcial (Referência do C#)
Um membro parcial tem uma declaração declarante e, com frequência, uma declaração de implementação. A declaração declarante não inclui um corpo. A declaração de implementação fornece 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 origem. Tipos e membros parciais fornecem uma maneira para desenvolvedores humanos escreverem parte de um tipo enquanto as ferramentas escrevem as outras partes. Se o desenvolvedor não fornecer uma declaração de implementação opcional, o compilador poderá remover a declaração declarante na hora da compilação. As seguintes condições são aplicáveis a membros parciais:
- As declarações devem começar com a palavra-chave contextual partial.
- As assinaturas em ambas as partes do tipo parcial devem ser correspondentes.
A palavra-chave partial
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 precisa ter uma declaração de implementação nos seguintes casos:
- Ele não tem modificadores de acessibilidade (incluindo o padrão
private
). - Ele retorna
void
. - Ele 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, método public virtual partial void
) 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, uma expressão regular pode ser definida 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ões regulares 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 mostra regras de combinação das duas declarações:
- Correspondências de assinatura: em geral, as assinaturas para as declarações declarantes e de implementação devem coincidir. Isso inclui o modificador de acessibilidade em métodos, propriedades, indexadores e acessadores individuais. Inclui os modificadores de tipo de parâmetro e ref-kind em todos os parâmetros. O tipo de retorno e qualquer modificador ref-kind devem corresponder. Os nomes dos membros da tupla devem corresponder. No entanto, algumas regras são flexíveis:
- As declarações declarantes e implementação podem ter configurações de anotações anuláveis. O que significa que um pode ser sem reconhecimento de anulável e o outro com anulável habilitado.
- As diferenças de nulidade que não envolvem sem reconhecimento de anulável geram um aviso.
- Os valores de parâmetro padrão não precisam corresponder. O compilador emitirá um aviso se a declaração de implementação de um método ou indexador declarar um valor de parâmetro padrão.
- O compilador emite um aviso quando os nomes de parâmetro não correspondem. O IL emitido contém os nomes de parâmetro 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 as declarações declarantes e de execução incluírem comentários de documentação, os comentários da declaração de implementação serão incluídos. No exemplo anterior, os comentários da documentação incluem:
- Para a propriedade
Capacity
, os comentários são retirados da declaração de implementação. Os comentários da declaração de implementação são utilizados quando ambas as declarações têm comentários///
. - Para o indexador, os comentários são obtidos da declaração declarante. A declaração de implementação não inclui comentários
///
. - No que diz respeito a
TryGetAt
, as observações são retiradas da declaração de implementação. A declaração declarante não inclui nenhum comentário///
. - O XML gerado tem comentários de documentação para todos os membros
public
.
- Para a propriedade
- A maioria das declarações de atributos é combinada. No entanto, todos os atributos de informações do chamador são definidos com
AllowMultiple=false
. O compilador reconhece qualquer atributo de informações do chamador na declaração de implementação. Todos os atributos de informações do chamador na declaração de implementação são ignorados. O compilador emitirá um aviso se você adicionar atributos de informações do chamador na declaração de implementação.