Condividi tramite


Classi e metodi parziali (Guida per programmatori C#)

È possibile suddividere la definizione di una classe o una struttura, di un'interfaccia o di un metodo tra due o più file di origine. Ogni file di origine contiene una sezione della definizione di tipo o di metodo e tutte le parti vengono combinate al momento della compilazione dell'applicazione.

Classi parziali

La suddivisione della definizione di una classe è consigliabile in diverse situazioni:

  • Quando si lavora su progetti di grandi dimensioni, la distribuzione di una classe in file distinti ne consente l'utilizzo simultaneo da parte di più programmatori.

  • Quando si utilizza un'origine generata automaticamente, è possibile aggiungere codice alla classe senza dover ricreare il file di origine. In Visual Studio questo approccio viene utilizzato per la creazione di Windows Form, codice wrapper di servizi Web e così via. È possibile creare codice che utilizza queste classi senza dover modificare il file creato in Visual Studio.

  • Per suddividere la definizione di una classe, utilizzare il modificatore della parola chiave partial, come illustrato di seguito:

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

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

La parola chiave partial indica che è possibile definire altre parti della classe, della struttura o dell'interfaccia all'interno dello spazio dei nomi. Tutte le parti devono utilizzare la parola chiave partial, nonché essere disponibili in fase di compilazione in modo da formare il tipo finale. Tutte le parti devono inoltre avere lo stesso livello di accessibilità, ad esempio public, private e così via.

Se una parte viene dichiarata come astratta, l'intero tipo verrà considerato astratto. Se una parte viene dichiarata come sealed, l'intero tipo verrà considerato sealed. Se una parte dichiara un tipo base, l'intero tipo erediterà la classe.

Tutte le parti che specificano una classe di base devono concordare, tuttavia le parti che omettono una classe di base ereditano comunque il tipo base. Le parti possono specificare interfacce di base differenti. In tal caso, il tipo finale implementerà tutte le interfacce elencate da tutte le dichiarazioni parziali. Qualsiasi membro di classe, struttura o interfaccia dichiarato in una definizione parziale risulta disponibile per tutte le altre parti. Il tipo finale rappresenta la combinazione di tutte le parti in fase di compilazione.

Nota

Il modificatore partial non è disponibile per le dichiarazioni di delegato o di enumerazione.

Nell'esempio seguente viene illustrato che i tipi annidati possono essere parziali, anche se non lo è il tipo all'interno del quale sono annidati.

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

In fase di compilazione gli attributi delle definizioni di tipi parziali verranno uniti. Si considerino ad esempio le seguenti dichiarazioni:

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

Sono equivalenti alle dichiarazioni seguenti:

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

I seguenti elementi vengono uniti da tutte le definizioni di tipi parziali:

  • commenti XML

  • interfacce

  • attributi di parametri di tipo generico

  • attributi di classi

  • membri

Si considerino ad esempio le seguenti dichiarazioni:

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

Sono equivalenti alle dichiarazioni seguenti:

class Earth : Planet, IRotate, IRevolve { }

Restrizioni

Quando si utilizzano le definizioni parziali di classi, è necessario rispettare diverse regole:

  • Tutte le definizioni di tipi parziali destinate a essere parti dello stesso tipo devono essere modificate con partial. Le seguenti dichiarazioni di classe generano ad esempio un errore:

    public partial class A { }
    //public class tcA { }  // Error, must also be marked partial
    
  • È possibile specificare il modificatore partial subito prima delle parole chiave class, struct o interface.

  • I tipi parziali annidati sono consentiti nelle definizioni di tipi parziali, come illustrato nell'esempio seguente:

    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
  • Tutte le definizioni di tipi parziali destinate a essere parti dello stesso tipo devono essere definite nello stesso assembly e nello stesso modulo (file EXE o DLL). Le definizioni parziali non possono estendersi su più moduli.

  • Il nome della classe e i parametri di tipo generico devono corrispondere in tutte le definizioni di tipi parziali. I tipi generici possono essere parziali. In ogni dichiarazione parziale è necessario utilizzare gli stessi nomi di parametri nello stesso ordine.

  • Le parole chiave riportate di seguito sono facoltative in una definizione di tipi parziali. Tuttavia, se presenti in una definizione, tali parole chiave non possono essere in conflitto con quelle specificate in un'altra definizione parziale per lo stesso tipo:

Esempio 1

Oggetto di descrizione

Nell'esempio seguente i campi e il costruttore della classe CoOrds vengono dichiarati in una definizione parziale di classe, mentre il membro PrintCoOrds viene dichiarato in un'altra definizione parziale di classe.

Codice

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

Esempio 2

Oggetto di descrizione

Nell'esempio riportato di seguito viene dimostrato che è anche possibile sviluppare strutture e interfacce parziali.

Codice

partial interface ITest
{
    void Interface_Test();
}

partial interface ITest
{
    void Interface_Test2();
}

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}

Metodi parziali

Una classe o struttura parziale può contenere un metodo parziale. Una parte della classe contiene la firma del metodo. Un'implementazione facoltativa può essere definita nella stessa parte o in un'altra parte. Se l'implementazione non viene fornita, il metodo e tutte le chiamate al metodo vengono rimosse in fase di compilazione.

I metodi parziali consentono all'implementatore di una parte di una classe di definire un metodo, simile a un evento. L'implementatore dell'altra parte della classe può decidere se implementare il metodo o meno. Se il metodo non viene implementato, il compilatore rimuove la firma del metodo e tutte le chiamate al metodo. Le chiamate al metodo, inclusi eventuali risultati che derivassero dalla valutazione di argomenti nelle chiamate, non hanno alcun effetto in fase di esecuzione. Pertanto, il codice nella classe parziale può utilizzare liberamente un metodo parziale, anche se non viene fornita l'implementazione. Non verranno generati errori in fase di compilazione o errori di runtime se il metodo viene chiamato ma non implementato.

I metodi parziali sono particolarmente utili per personalizzare il codice generato. Consentono di riservare un nome e una firma del metodo, in modo che il codice generato possa chiamare il metodo ma spetta allo sviluppatore decidere se implementare il metodo. Analogamente alle classi parziali, i metodi parziali consentono di poter utilizzare il codice creato da un generatore di codice con il codice creato da un sviluppatore senza alcun costo in fase di esecuzione.

Una dichiarazione di metodo parziale è costituita da due parti: la definizione e l'implementazione, che possono trovarsi in parti separate di una classe parziale o nella stessa parte. Se non è presente la dichiarazione di implementazione, il compilatore ottimizza la dichiarazione di definizione e tutte le chiamate al metodo.

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

// Implementation in file2.cs
partial void onNameChanged()
{
  // method body
}
  • Le dichiarazioni di metodi parziali devono iniziare con la parola chiave contestuale partial e il metodo deve restituire void.

  • I metodi parziali possono contenere il parametro ref ma non il parametro out.

  • I metodi parziali sono implicitamente private e pertanto non possono essere virtual.

  • I metodi parziali non possono essere extern, perché la presenza del corpo determina se è in corso una definizione o un'implementazione.

  • I metodi parziali possono contenere modificatori static e unsafe.

  • I metodi parziali possono essere generici. I vincoli vengono inseriti nella dichiarazione di definizione del metodo parziale e possono essere ripetuti facoltativamente nella dichiarazione di implementazione. I nomi dei parametri e dei parametri di tipo non devono essere uguali nella dichiarazione di implementazione e in quella di definizione.

  • È possibile creare un delegato di un metodo parziale che è stato definito e implementato, ma non di un metodo parziale che è stato solo definito.

Specifiche del linguaggio C#

Per ulteriori informazioni, vedere la Specifiche del linguaggio C#. La specifica del linguaggio è la fonte ufficiale per la sintassi e l'utilizzo di C#.

Vedere anche

Riferimenti

Classi (Guida per programmatori C#)

Struct (Guida per programmatori C#)

Interfacce (Guida per programmatori C#)

parziale (Tipo) (Riferimenti per C#)

Concetti

Guida per programmatori C#