Condividi tramite


Classi e metodi parziali (Guida per programmatori C#)

È possibile suddividere la definizione di una classe, uno struct, un'interfaccia o un metodo tra due o più file di origine. Ogni file di origine contiene una sezione della definizione di tipo o 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:

  • La dichiarazione di una classe in file distinti ne consente l'uso simultaneo da parte di più programmatori.
  • È possibile aggiungere codice alla classe senza dover ricreare il file di origine che include l'origine generata automaticamente. Visual Studio usa questo approccio per la creazione di Windows Form, codice wrapper di servizi Web e così via. È possibile creare codice che usa queste classi senza dover modificare il file creato da Visual Studio.
  • I generatori di origine possono generare funzionalità aggiuntive in una classe.

Per suddividere la definizione di una classe, usare 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, dello struct o dell'interfaccia nello spazio dei nomi. Tutte le parti devono usare la parola chiave partial ed essere disponibili in fase di compilazione in modo da formare il tipo finale. Tutte le parti devono anche 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 eredita la classe.

Tutte le parti che specificano una classe base devono concordare, tuttavia le parti che omettono una classe base ereditano comunque il tipo base. Le parti possono specificare interfacce di base differenti e il tipo finale implementa tutte le interfacce elencate da tutte le dichiarazioni parziali. Tutti i membri di classe, struttura o interfaccia dichiarati in una definizione parziale sono disponibili 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.

L'esempio seguente illustra che i tipi nidificati possono essere parziali, anche se non lo è il tipo all'interno del quale sono nidificati.

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

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

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

[SerializableAttribute]
partial class Moon { }

[ObsoleteAttribute]
partial class Moon { }

Sono equivalenti alle dichiarazioni seguenti:

[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }

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

  • Commenti in XML
  • interfaces
  • attributi di parametri di tipo generico
  • attributi class
  • membri

Si considerino ad esempio le dichiarazioni seguenti:

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

Sono equivalenti alle dichiarazioni seguenti:

class Earth : Planet, IRotate, IRevolve { }

Restrizioni

Quando si usano le definizioni di classe parziali è necessario rispettare diverse regole:

  • Tutte le definizioni di tipi parziali destinate a essere parti dello stesso tipo devono essere modificate con partial. Ad esempio, le dichiarazioni di classe seguenti generano un errore:
    public partial class A { }
    //public class A { }  // Error, must also be marked partial
    
  • Il modificatore partial può essere specificato solo 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 con estensione exe o dll). Le definizioni parziali non possono estendersi in 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 usare gli stessi nomi di parametri nello stesso ordine.
  • Le parole chiave seguenti sono facoltative in una definizione di tipo parziale. 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:

Per altre informazioni, vedere Vincoli sui parametri di tipo.

Esempi

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.

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

L'esempio seguente dimostra che è anche possibile sviluppare struct e interfacce parziali.

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 uno struct parziale può contenere un metodo parziale. Una parte della classe contiene la firma del metodo. Un'implementazione può essere definita nella stessa parte o in un'altra.

Un'implementazione non è necessaria per un metodo parziale quando la firma rispetta le regole seguenti:

  • La dichiarazione non include modificatori di accesso. Il metodo include l'accesso private per impostazione predefinita.
  • Il tipo restituito è void.
  • Nessuno dei parametri include il modificatore out.
  • La dichiarazione del metodo non può includere nessuno dei modificatori seguenti:

Il metodo e tutte le chiamate al metodo vengono rimosse in fase di compilazione quando non è presente alcuna implementazione.

Qualsiasi metodo che non è conforme a tutte le restrizioni (ad esempio, il metodo public virtual partial void), deve fornire un'implementazione. Tale implementazione potrebbe essere fornita da un generatore di origine.

I metodi parziali consentono all'implementatore della parte di una classe di dichiarare un metodo. L'implementatore di un'altra parte della classe può definire questo metodo. Questa separazione è utile in due scenari: modelli che generano codice boilerplate e generatori di origine.

  • Codice modello: il modello riserva un nome e una firma del metodo così che il codice generato possa chiamare il metodo. Questi metodi seguono le restrizioni che consentono allo sviluppatore di decidere se implementare o meno il metodo. 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ò usare liberamente un metodo parziale, anche se non viene specificata l'implementazione. Non vengono generati errori in fase di compilazione o errori di runtime se il metodo viene chiamato ma non implementato.
  • Generatori di origine: i generatori di origine forniscono un'implementazione per i metodi. Lo sviluppatore umano può aggiungere la dichiarazione del metodo (spesso con gli attributi letti dal generatore di origine). Lo sviluppatore può scrivere codice che chiama tali metodi. Il generatore di origine viene eseguito durante la compilazione e fornisce l'implementazione. In questo scenario le restrizioni per i metodi parziali che potrebbero non essere implementati spesso non vengono seguite.
// Definition in file1.cs
partial void OnNameChanged();

// Implementation in file2.cs
partial void OnNameChanged()
{
  // method body
}
  • Le dichiarazioni del metodo parziale devono iniziare con la parola chiave contestuale parziale.
  • Le firme del metodo parziale in entrambe le parti del tipo parziale devono corrispondere.
  • I metodi parziali possono contenere modificatori static e unsafe.
  • I metodi parziali possono essere generici. I vincoli devono essere gli stessi nella dichiarazione del metodo di definizione e in quella 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 definito e implementato, ma non di un metodo parziale per cui non esiste un'implementazione.

Specifiche del linguaggio C#

Per altre informazioni, vedere Tipi parziali e Metodi parziali in Specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#. Le funzionalità aggiuntive per i metodi parziali sono definite nella specifica della funzionalità.

Vedi anche