Miembro parcial (referencia de C#)

Un miembro parcial tiene una declaración declaratoria y, a menudo, una declaración de implementación. La declaración declaratoria no incluye un cuerpo. La declaración de implementación proporciona el cuerpo del miembro. Los miembros parciales permiten a los diseñadores de clases proporcionar enlaces de miembro que se pueden implementar mediante herramientas como generadores de origen. Los tipos parciales y los miembros proporcionan una manera de que los desarrolladores humanos escriban parte de un tipo mientras las herramientas escriben otras partes del tipo. Si el desarrollador no proporciona una declaración de implementación opcional, el compilador puede quitar la declaración declarante en tiempo de compilación. Se aplican las siguientes condiciones a los miembros parciales:

  • Las declaraciones deben comenzar con la palabra clave contextual partial.
  • Las signaturas de ambas partes del tipo parcial deben coincidir.

La palabra clave partial no está permitida en constructores, finalizadores, operadores sobrecargados o declaraciones de eventos. Antes de C# 13, partial no estaba permitido en propiedades o indizadores.

No es necesario que un método parcial tenga una declaración de implementación en los casos siguientes:

Cualquier miembro que no cumpla todas estas restricciones (por ejemplo, el método public virtual partial void) debe proporcionar una implementación. Las propiedades parciales y los indizadores deben tener una implementación.

En el ejemplo siguiente se muestra un método parcial que se ajusta a las restricciones 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}");
}

Los miembros parciales también pueden ser útiles en combinación con los generadores de código fuente. Por ejemplo, se podría definir una expresión regular con el siguiente patrón:

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

En el ejemplo anterior se muestra un método parcial que debe tener una declaración de implementación. Como parte de una compilación, el generador fuente de expresiones regulares crea la declaración de implementación.

En el ejemplo siguiente se muestra una declaración declaratoria y una declaración de implementación para una clase. Dado que el tipo de valor devuelto del método no es void (es string) y su acceso es public, el método debe tener una declaración de implementación:

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

En el ejemplo anterior se muestran las reglas sobre cómo se combinan las dos declaraciones:

  • Coincidencias de firma: en general, las firmas para las declaraciones declarantes e implementaciones deben coincidir. Esto incluye el modificador de accesibilidad en métodos, propiedades, indizadores y descriptores de acceso individuales. Incluye los modificadores de tipo de parámetro y ref-kind en todos los parámetros. El tipo de valor devuelto y cualquier modificador de tipo ref deben coincidir. Los nombres de miembro de tupla deben coincidir. Sin embargo, algunas reglas son flexibles:
    • La declaración e implementación de declaraciones puede tener diferentes valores de anotaciones que aceptan valores NULL. Lo que significa que uno puede ser ajeno a los valores NULL y el otro habilitado para valores NULL.
    • Las diferencias de nulabilidad que no implican una nulabilidad ajena generan una advertencia.
    • No es necesario que los valores de parámetro predeterminados coincidan. El compilador emite una advertencia si la declaración de implementación de un método o indizador declara un valor de parámetro predeterminado.
    • El compilador emite una advertencia cuando los nombres de parámetro no coinciden. El IL emitido contiene los nombres de parámetro de declaración declarante.
  • Comentarios de documentación: los comentarios de documentación se pueden incluir en cualquiera de las declaraciones. Si las declaraciones declarantes y de implementación contienen comentarios de documentación, se incluyen los comentarios de la declaración de implementación. En el ejemplo anterior, los comentarios de documentación incluyen:
    • Para la propiedad Capacity, los comentarios se toman de la declaración de implementación. Los comentarios de declaración de implementación se usan cuando ambas declaraciones tienen comentarios ///.
    • Para el indizador, los comentarios se toman de la declaración declarante. La declaración de implementación no incluye ningún comentario ///.
    • Para TryGetAt, los comentarios se toman de la declaración de implementación. La declaración declaratoria no incluye ningún comentario ///.
    • El XML generado tiene comentarios de documentación para todos los miembros public.
  • La mayoría de las declaraciones de atributo se combinan. Sin embargo, todos los atributos de información del autor de la llamada se definen con AllowMultiple=false. El compilador reconoce cualquier atributo de información del autor de la llamada en la declaración declarante. Se omiten todos los atributos de información del autor de la llamada en la declaración de implementación. El compilador emite una advertencia si agrega atributos de información del autor de la llamada en la declaración de implementación.

Consulte también