Teilen über


Teilmitglied (C#-Referenz)

Ein Teilmitglied verfügt über eine deklarierende Deklaration und häufig eine Implementierungsdeklaration. Die deklarierende Deklaration enthält keinen Textkörper. Die Implementierungsdeklaration stellt den Text des Mitglieds bereit. Mit partiellen Mitgliedern können Klassen-Designer Mitglieder-Hooks bereitstellen, die durch Tools wie Quellgeneratoren implementiert werden können. Teiltypen und Mitglieder ermöglichen es menschlichen Entwicklern, einen Teil eines Typs zu schreiben, während Tools andere Teile des Typs schreiben. Wenn der Entwickler keine optionale Implementierungsdeklaration angibt, kann der Compiler die deklarierende Deklaration zur Kompilierungszeit entfernen. Die folgenden Bedingungen gelten für Teilmitglieder:

  • Deklarationen müssen mit dem Kontextschlüsselwort partial beginnen.
  • Die Signaturen in beiden Teilen des partiellen Typs müssen übereinstimmen.

Das partial-Schlüsselwort ist für Konstruktoren, Finalizer, überladene Operatoren oder Ereignisdeklarationen nicht zulässig. Vor C# 13 war partial für Eigenschaften oder Indexer nicht zulässig.

Eine Teilmethode erfordert in den folgenden Fällen keine Implementierungsdeklaration:

  • Sie verfügt über keine Zugriffsmodifizierer (einschließlich der Standardeinstellung private).
  • Er gibt void zurück.
  • Sie verfügt über keine out-Parameter.
  • Sie enthält keine der folgenden Modifizierer virtual, override, sealed, new oder extern.

Jedes Mitglied, das nicht allen Einschränkungen entspricht (z. B. die public virtual partial void-Methode), muss eine Implementierung bereitstellen. Partielle Eigenschaften und Indexer müssen über eine Implementierung verfügen.

Das folgende Beispiel zeigt eine Teilmethode, die den vorherigen Einschränkungen entspricht:

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

Teilmitglieder können auch in Kombination mit Quell-Generatoren nützlich sein. Beispielsweise könnte ein regulärer Ausdruck mithilfe des folgenden Musters definiert werden:

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

Das vorangehende Beispiel zeigt eine Teilmethode, die über eine Implementierungsdeklaration verfügen muss. Im Rahmen eines Builds erstellt der Quellengenerator für reguläre Ausdrücke die Implementierungsdeklaration.

Das folgende Beispiel zeigt eine deklarierende Deklaration und eine Implementierungsdeklaration für eine Klasse. Da der Rückgabetyp der Methode nicht void ist, (sondern string) und sein Zugriff public lautet, muss die Methode über eine Implementierungsdeklaration verfügen:

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

Im vorherigen Beispiel werden Regeln für die Kombination der beiden Deklarationen veranschaulicht:

  • Signatur-Übereinstimmungen: Im Allgemeinen müssen die Signaturen für die deklarierenden und implementierenden Deklarationen übereinstimmen. Dazu gehören Barrierefreiheitsmodifizierer für Methoden, Eigenschaften, Indexer und einzelne Accessoren. Sie enthalten die Parametertyp- und Referezenztypmodifizierer für alle Parameter. Der Rückgabetyp und jeder Referenztypmodifizierer müssen übereinstimmen. Tuple-Mitgliedsnamen müssen übereinstimmen. Einige Regeln sind jedoch flexibel:
    • Die Deklarierung und Implementierung von Deklarationen können unterschiedliche nullable-Annotationseinstellungen aufweisen. Das bedeutet, dass der eine die Eigenschaft Nullwerte zulassend-nicht beachtend und der andere die Eigenschaft Nullwerte zulassend aufweisen können.
    • NULL-Zulässigkeitsunterschiede, die die Eigenschaft Nullwerte zulassen - nicht beachtend aufweisen, generieren eine Warnung.
    • Standardwerte müssen nicht übereinstimmen. Der Compiler gibt eine Warnung aus, wenn die implementierenden Deklaration einer Methode oder eines Indexers einen Standardwert deklariert.
    • Der Compiler gibt eine Warnung aus, wenn Parameternamen nicht übereinstimmen. Die ausgegebene IL enthält die Parameternamen der deklarierenden Deklaration.
  • Dokumentationskommentare: können aus einer der beiden Deklarationen eingeschlossen werden. Wenn sowohl die deklarierenden als auch die Implementierungsdeklaration Dokumentationskommentare enthalten, werden die Kommentare aus der Implementierungsdeklaration eingeschlossen. Im vorherigen Beispiel enthalten die Dokumentationskommentare:
    • Für die Capacity-Eigenschaft werden die Kommentare aus der Implementierungsdeklaration entnommen. Die Implementierungsdeklarationskommentare werden verwendet, wenn beide Deklarationen ///-Kommentare enthalten.
    • Für den Indexer werden die Kommentare aus der deklarierenden Deklaration entnommen. Die Implementierungsdeklaration enthält keine ///-Kommentare.
    • Für TryGetAt werden die Kommentare aus der implementierenden Deklaration entnommen. Die deklarierende Deklaration enthält keine ///-Kommentare.
    • Die generierte XML enthält Dokumentationskommentare für alle public-Mitglieder.
  • Die meisten Attributdeklarationen werden kombiniert. Alle Aufruferinfoattribute werden jedoch mit AllowMultiple=false definiert. Der Compiler erkennt jedes Aufruferinfoattribut für die deklarierende Deklaration. Alle Aufruferinformationenattribute für die Implementierungsdeklaration werden ignoriert. Der Compiler gibt eine Warnung aus, wenn Sie Aufruferinfoattribute für die Implementierungsdeklaration hinzufügen.

Siehe auch