Membro parziale (Riferimenti per C#)

Un membro parziale ha una dichiarazione dichiarativa e spesso una dichiarazione di implementazione. La dichiarazione dichiarativa non include un corpo. La dichiarazione di implementazione fornisce il corpo del membro. I membri parziali consentono alle finestre di progettazione classi di fornire hook membro che possono essere implementati tramite strumenti come i generatori di origine. I tipi e i membri parziali consentono agli sviluppatori umani di scrivere parte di un tipo mentre gli strumenti scrivono altre parti del tipo. Se lo sviluppatore non fornisce una dichiarazione di implementazione facoltativa, il compilatore può rimuovere la dichiarazione dichiarativa in fase di compilazione. Ai membri parziali si applicano le condizioni seguenti:

  • Le dichiarazioni devono iniziare con la parola chiave contestuale parziale.
  • Le firme nelle due parti del tipo parziale devono corrispondere.

La parola chiave partial non è consentita in costruttori, finalizzatori, operatori di overload o dichiarazioni di eventi. Prima di C# 13, partial non era consentito per le proprietà o gli indicizzatori.

Un metodo parziale non è necessario per avere una dichiarazione di implementazione nei casi seguenti:

Qualsiasi membro che non è conforme a tutte queste restrizioni (ad esempio, il metodo public virtual partial void), deve fornire un'implementazione. Le proprietà parziali e gli indicizzatori devono avere un'implementazione.

L'esempio seguente illustra un metodo parziale conforme alle restrizioni precedenti:

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}");
}

I membri parziali possono essere utili anche in combinazione con i generatori di origine. Ad esempio, è possibile definire un'espressione regolare usando il modello seguente:

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
        }
    }
}

L'esempio precedente mostra un metodo parziale che deve avere una dichiarazione di implementazione. Come parte di una compilazione, il generatore di origine delle espressioni regolari crea la dichiarazione di implementazione.

Nell'esempio seguente, viene illustrata una dichiarazione dichiarativa e una dichiarazione di implementazione per una classe . Poiché il tipo restituito del metodo non è voidstring) e il relativo accesso è public, il metodo deve avere una dichiarazione di implementazione:

// 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;
    }
}

L'esempio precedente illustra le regole sulla combinazione delle due dichiarazioni:

  • Corrispondenze di firma: in generale, le firme per la dichiarazione e l'implementazione delle dichiarazioni devono corrispondere. Sono inclusi i modificatori di accessibilità per metodi, proprietà, indicizzatori e singole funzioni di accesso. Include il tipo di parametro e i modificatori di tipo ref in tutti i parametri. Il tipo restituito e qualsiasi modificatore di tipo ref devono corrispondere. I nomi dei membri della tupla devono corrispondere. Tuttavia, alcune regole sono flessibili:
    • Le dichiarazioni dichiarativa e di implementazione possono avere impostazioni di annotazioni che emettono valori nulli diversi. Questo significa che una può essere incurante dei valori Null e l'altra abilitata per nullable.
    • Le differenze di supporto dei valori Null che non comportano l'indipendenza dai valori Null generano un avviso.
    • I valori dei parametri predefiniti non devono corrispondere. Il compilatore genera un avviso se la dichiarazione di implementazione di un metodo o di un indicizzatore dichiara un valore di parametro predefinito.
    • Il compilatore genera un avviso quando i nomi dei parametri non corrispondono. Il codice IL generato contiene i nomi dei parametri della dichiarazione dichiarativa.
  • Commenti alla documentazione: i commenti alla documentazione possono essere inclusi da una delle due dichiarazioni. Se entrambe le dichiarazioni dichiarativa e di implementazione includono commenti alla documentazione, vengono inclusi i commenti della dichiarazione di implementazione. Nell'esempio precedente, i commenti alla documentazione includono:
    • Per la proprietà Capacity, i commenti vengono ricavati dalla dichiarazione di implementazione. I commenti della dichiarazione di implementazione vengono utilizzati quando entrambe le dichiarazioni hanno commenti ///.
    • Per l'indicizzatore, i commenti vengono ricavati dalla dichiarazione dichiarativa. La dichiarazione di implementazione non include commenti ///.
    • Per TryGetAt, i commenti provengono dalla dichiarazione di implementazione. La dichiarazione dichiarativa non include commenti ///.
    • Il codice XML generato contiene commenti alla documentazione per tutti i membri public.
  • La maggior parte delle dichiarazioni di attributo viene combinata. Tuttavia, tutti gli attributi delle informazioni sul chiamante vengono definiti con AllowMultiple=false. Il compilatore riconosce qualsiasi attributo info chiamante nella dichiarazione dichiarativa. Tutti gli attributi delle informazioni sul chiamante nella dichiarazione di implementazione vengono ignorati. Il compilatore genera un avviso se si aggiungono attributi di informazioni sul chiamante nella dichiarazione di implementazione.

Vedi anche