Clases y métodos parciales (Guía de programación de C#)

Es posible dividir la definición de una clase, struct, interfaz o método en dos o más archivos de código fuente. Cada archivo de código fuente contiene una sección de la definición de tipos o métodos, y todas las partes se combinan cuando se compila la aplicación.

Clases parciales

Existen diversas situaciones en las que es conveniente dividir una definición de clase:

  • Al trabajar en proyectos grandes, el hecho de dividir una clase en archivos independientes permite que varios programadores trabajen al mismo tiempo con ella.

  • Al trabajar con un código fuente generado automáticamente, se puede agregar el código a la clase sin tener que volver a crear el archivo de código fuente. Visual Studio utiliza este enfoque al crear formularios Windows Forms, código contenedor de un servicio Web, etc. Se puede crear código que utilice estas clases sin tener que modificar el archivo creado por Visual Studio.

  • Para dividir una definición de clase, utilice el modificador partial, como se muestra a continuación:

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

La palabra clave partial indica que otras partes de la clase, struct o interfaz se pueden definir en el espacio de nombres. Todas las partes deben utilizar la palabra clave partial. Todas las partes deben estar disponibles en tiempo de compilación para formar el tipo final. Todas las partes deben tener la misma accesibilidad, ya sea public, private, etc.

Si alguna parte se declara abstracta, todo el tipo se considera abstracto. Si alguna parte se declara sealed, todo el tipo se considera sealed. Si alguna parte declara un tipo base, todo el tipo hereda esa clase.

Todas las partes que especifican una clase base deben concordar, pero las partes que omiten una clase base heredan igualmente el tipo base. Las partes pueden especificar diferentes interfaces base, pero el tipo final implementa todas las interfaces mostradas por todas las declaraciones parciales. Cualquier miembro de clase, struct o interfaz declarado en una definición parcial está disponible para todas las demás partes. El tipo final es la combinación de todas las partes en tiempo de compilación.

NotaNota

El modificador partial no está disponible en declaraciones de delegado o enumeración.

El siguiente ejemplo muestra que los tipos anidados pueden ser parciales, aunque el tipo en el que están anidados no sea propiamente parcial.

class Container
{
    partial class Nested
    {
        void Test() { }
    }
    partial class Nested
    {
        void Test2() { }
    }
}

En tiempo de compilación, se combinan los atributos de definiciones de tipo parcial. Por ejemplo, consideremos las siguientes declaraciones:

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

Son equivalentes a las declaraciones siguientes:

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

Los siguientes elementos se combinan a partir de todas las definiciones de tipo parcial:

  • comentarios XML

  • interfaces

  • atributos de parámetro de tipo genérico

  • atributos de clase

  • miembros

Por ejemplo, consideremos las siguientes declaraciones:

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

Son equivalentes a las declaraciones siguientes:

class Earth : Planet, IRotate, IRevolve { }

Restricciones

Existen varias reglas que se deben seguir al trabajar con definiciones de clase parciales:

  • Todas las definiciones de tipo parcial creadas para ser parte del mismo tipo deben modificarse con partial. Por ejemplo, las siguientes declaraciones de clase generan un error:

    public partial class A { }
    //public class A { }  // Error, must also be marked partial
    
  • El modificador partial sólo puede aparecer inmediatamente antes de las palabras clave class, struct o interface.

  • Se permiten tipos parciales anidados en definiciones de tipo parcial, como se muestra en el siguiente ejemplo:

    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
  • Todas las definiciones de tipo parcial que deben ser parte del mismo tipo deben definirse en el mismo ensamblado y el mismo módulo (archivo .exe o .dll). Las definiciones parciales no pueden abarcar varios módulos.

  • Los parámetros de nombre de clase y tipo genérico deben coincidir en todas las definiciones de tipo parcial. Los tipos genéricos pueden ser parciales. Todas las declaraciones parciales deben utilizar los mismos nombres de parámetro en el mismo orden.

  • Las siguientes palabras clave en una definición de tipo parcial son opcionales, pero si hay alguna en una definición de tipo parcial, no puede entrar en conflicto con las palabras clave especificadas en otra definición parcial para el mismo tipo:

Ejemplo 1

Descripción

En el siguiente ejemplo, los campos y el constructor de la clase, CoOrds, se declaran en una definición de clase parcial, mientras que el miembro PrintCoOrds se declara en otra definición de clase parcial.

Código

public partial class CoOrds
{
    private int x;
    private int y;

    public CoOrds(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

public partial class CoOrds
{
    public void PrintCoOrds()
    {
        Console.WriteLine("CoOrds: {0},{1}", x, y);
    }

}

class TestCoOrds
{
    static void Main()
    {
        CoOrds myCoOrds = new CoOrds(10, 15);
        myCoOrds.PrintCoOrds();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
// Output: CoOrds: 10,15

Ejemplo 2

Descripción

El siguiente ejemplo muestra que también se pueden desarrollar structs e interfaces parciales.

Código

partial interface ITest
{
    void Interface_Test();
}

partial interface ITest
{
    void Interface_Test2();
}

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}

Métodos Partial

Una clase o struct parcial puede contener un método parcial. Una parte de la clase contiene la firma del método. Una implementación opcional se puede definir en la misma parte u otra parte. Si no se proporciona la implementación, el método y todas las llamadas a él se quitan en tiempo de compilación.

Los métodos parciales permiten al implementador de una parte de una clase definir un método, de forma similar a un evento. El implementador de la otra parte de la clase puede decidir si implementar el método o no. Si no se implementa el método, el compilador quita la firma del método y todas las llamadas al método. Las llamadas al método, incluidos los resultados ocurridos a partir de la evaluación de los argumentos en las llamadas, no tienen ningún efecto en tiempo de ejecución. Por consiguiente, cualquier código en la clase parcial puede utilizar libremente un método parcial, aun cuando no se proporcione la implementación. No se producirá ningún error en tiempo de compilación o en tiempo de ejecución si se realizan llamadas al método y éste no está implementado.

Los métodos parciales son especialmente útiles como una manera de personalizar el código generado. Permiten reservar un nombre y una firma de método, de modo que el código generado pueda llamar al método y el programador pueda decidir si lo implementa o no. De forma muy similar a las clases parciales, los métodos parciales permiten que el código creado por un generador de código y el código creado por un programador humano puedan funcionar juntos sin costes en tiempo de ejecución.

Una declaración de método parcial consta de dos partes: la definición y la implementación. Éstas pueden estar en partes independientes de una clase parcial o en la misma parte. Si no existe ninguna declaración de implementación, el compilador quita la declaración de definición y todas las llamadas al método.

// Definition in file1.cs
partial void onNameChanged();

// Implementation in file2.cs
partial void onNameChanged()
{
  // method body
}
  • Las declaraciones de método parciales deben comenzar con la palabra clave contextual partial, y el método debe devolver void.

  • Los métodos parciales pueden tener parámetros ref, pero no out.

  • Los métodos parciales son implícitamente private y, por consiguiente, no pueden ser virtual.

  • Los métodos parciales no pueden ser extern, porque la presencia del cuerpo determina si son de definición o de implementación.

  • Los métodos parciales pueden tener modificadores static y unsafe.

  • Los métodos parciales pueden ser genéricos. Las restricciones se colocan en la declaración de método parcial que realiza la definición, y se pueden repetir opcionalmente en la parte de implementación. Los nombres de parámetros y parámetros de tipo no tienen que ser iguales en la declaración que realiza la implementación y en la que realiza la definición.

  • Se puede crear un delegado de un método parcial que se ha definido e implementado, pero no de un método parcial que solo se ha definido.

Especificación del lenguaje C#

Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.

Vea también

Referencia

Clases (Guía de programación de C#)

Structs (Guía de programación de C#)

Interfaces (Guía de programación de C#)

Tipo parcial (Referencia de C#)

Conceptos

Guía de programación de C#