Implementer klasser ved hjælp af delvise klasser
Det er muligt at opdele definitionen af en klasse eller metode over to eller flere kildefiler. Hver kildefil indeholder et afsnit af typen eller metodedefinitionen, og alle dele kombineres, når programmet kompileres.
Delvise klasser
Der er flere situationer, hvor det er ønskeligt at opdele en klassedefinition:
- Erklæring af en klasse over separate filer gør det muligt for flere programmører at arbejde på det på samme tid.
- Du kan føje kode til klassen uden at skulle genoprette den kildefil, der indeholder automatisk genereret kilde. Visual Studio bruger denne fremgangsmåde, når den opretter Windows Forms, webtjenesteombrydningskode osv. Du kan oprette kode, der bruger disse klasser, uden at skulle ændre den fil, der er oprettet af Visual Studio.
- Kildegeneratorer kan generere ekstra funktionalitet i en klasse.
Hvis du vil opdele en klassedefinition, skal du bruge den delvise nøgleordsændring. I praksis defineres hver delklasse typisk i en separat fil, hvilket gør det nemmere at administrere og udvide klassen over tid.
Følgende Employee eksempel viser, hvordan klassen kan opdeles på to filer: Employee_Part1.cs og Employee_Part2.cs.
// This is in Employee_Part1.cs
public partial class Employee
{
public void DoWork()
{
Console.WriteLine("Employee is working.");
}
}
// This is in Employee_Part2.cs
public partial class Employee
{
public void GoToLunch()
{
Console.WriteLine("Employee is at lunch.");
}
}
//Main program demonstrating the Employee class usage
public class Program
{
public static void Main()
{
Employee emp = new Employee();
emp.DoWork();
emp.GoToLunch();
}
}
// Expected Output:
// Employee is working.
// Employee is at lunch.
Nøgleordet partial angiver, at andre dele af klassen kan defineres i navneområdet. Alle delene skal bruge nøgleordet partial. Alle dele skal være tilgængelige på kompileringstidspunktet for at danne den endelige type. Alle delene skal have samme tilgængelighed, f.eks. public, privateosv.
Hvis en del erklæres abstrakt, betragtes hele typen som abstrakt. Hvis en del erklæres forseglet, anses hele typen for at være forseglet. Hvis en del deklarerer en basistype, arver hele typen den pågældende klasse.
Alle de dele, der angiver en basisklasse, skal være enige, men dele, der udelader en basisklasse, arver stadig basistypen. Dele kan angive forskellige basisgrænseflader, og den endelige type implementerer alle de grænseflader, der er angivet af alle de delvise erklæringer. Alle klasse-, struktur- eller grænseflademedlemmer, der er erklæret i en delvis definition, er tilgængelige for alle de andre dele. Den endelige type er kombinationen af alle delene på kompileringstidspunktet.
Seddel
partial-modifikatoren er ikke tilgængelig for delegerings- eller optællingserklæringer.
I følgende eksempel kan du se, at indlejrede typer kan være delvise, selvom den type, de er indlejret i, ikke er delvis i sig selv.
class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
På kompileringstidspunktet flettes attributter for definitioner af delvise typer. Du kan f.eks. overveje følgende erklæringer:
[SerializableAttribute]
partial class Moon { }
[ObsoleteAttribute]
partial class Moon { }
De svarer til følgende erklæringer:
[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }
Følgende flettes fra alle definitioner af delvise typer:
- XML-kommentarer. Men hvis begge erklæringer fra et delvist medlem indeholder kommentarer, er det kun kommentarerne fra det implementerende medlem, der medtages.
- Grænseflader
- generiske parameterattributter af typen
- klasseattributter
- medlemmer
Du kan f.eks. overveje følgende erklæringer:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
De svarer til følgende erklæringer:
class Earth : Planet, IRotate, IRevolve { }
Begrænsninger for definitioner af delvise klasser
Der er flere regler, du skal følge, når du arbejder med delvise klassedefinitioner:
Alle definitioner af delvise typer, der er beregnet til at være dele af samme type, skal ændres med delvise. Følgende klasseerklæringer genererer f.eks. en fejl:
public partial class A { } //public class A { } // Error, must also be marked partialDen delvise modifikator kan kun vises umiddelbart før nøgleordsklassen, -konstruktionen eller -grænsefladen.
Indlejrede delvise typer er tilladt i definitioner af delvise typer, som vist i følgende eksempel:
partial class ClassWithNestedClass { partial class NestedClass { } } partial class ClassWithNestedClass { partial class NestedClass { } }Alle definitioner af delvise typer, der er beregnet til at være dele af samme type, skal defineres i den samme assembly og det samme modul (.exe eller .dll fil). Delvise definitioner kan ikke strække sig over flere moduler.
Klassenavnet og standardtypeparametrene skal stemme overens med alle definitioner af delvise typer. Generiske typer kan være delvise. Hver delerklæring skal bruge de samme parameternavne i samme rækkefølge.
Følgende nøgleord i en definition af delvis type er valgfrie, men hvis de findes i én definition af delvis type, skal det samme angives i en anden delvis definition for den samme type:
- offentlig
- privat
- Beskyttet
- indre
- abstrakt
- Forseglet
- basisklasse
- ny modifikator (indlejrede dele)
- generiske begrænsninger
Implementer delvise klasser
I følgende eksempel erklæres felterne og konstruktøren for den Coords klasse i én delklassedefinition (Coords_Part1.cs), og metoden PrintCoords erklæres i en anden delvis klassedefinition (Coords_Part2.cs). Denne adskillelse viser, hvordan delvise klasser kan opdeles på tværs af flere filer for at gøre det nemmere at vedligeholde dem.
// This is in Coords_Part1.cs
public partial class Coords
{
private int x;
private int y;
public Coords(int x, int y)
{
this.x = x;
this.y = y;
}
}
// This is in Coords_Part2.cs
public partial class Coords
{
public void PrintCoords()
{
Console.WriteLine("Coords: {0},{1}", x, y);
}
}
// Main program demonstrating the Coords class usage
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
Delvise medlemmer
En delvis klasse kan indeholde et delvist medlem. En del af klassen indeholder medlemmets signatur. En implementering kan defineres i den samme del eller en anden del.
En implementering er ikke påkrævet for en delvis metode, når signaturen overholder følgende regler:
Erklæringen indeholder ingen adgangsændring. Metoden har som standard privat adgang.
Returtypen er
void.Ingen af parametrene har
out-modifikatoren.Metodeerklæringen kan ikke indeholde nogen af følgende modifikatorer:
- virtuel
- tilsidesætte
- Forseglet
- ny
- Extern
Metoden og alle kald til metoden fjernes på kompileringstidspunktet, når der ikke er nogen implementering.
Alle metoder, der ikke er i overensstemmelse med alle disse begrænsninger, herunder egenskaber og indeksering, skal levere en implementering. Denne implementering kan leveres af en kildegenerator. Delvise egenskaber kan ikke implementeres ved hjælp af egenskaber, der er implementeret automatisk. Compileren kan ikke skelne mellem en automatisk implementeret egenskab og erklæringserklæringen for en delvis egenskab.
Fra og med C# 13 kan gennemførelseserklæringen for en delvis egenskab bruge feltunderstøttede egenskaber til at definere gennemførelseserklæringen. En feltunderstøttet egenskab indeholder en præcis syntaks, hvor nøgleordet for feltet tilgår compilerens syntetiserede backing-felt for egenskaben. Du kan f.eks. skrive følgende kode:
// in file1.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get; set; }
}
// In file2.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get => field; set; }
}
Du kan bruge field enten i get- eller set-accessoren eller begge dele.
Vigtig
Nøgleordet field er en prøveversionsfunktion i C# 13. Du skal bruge .NET 9 og angive det <LangVersion> element til eksempelvisning i projektfilen for at kunne bruge det kontekstafhængige nøgleord field.
Du skal være forsigtig med at bruge nøgleordsfunktionen field i en klasse, der har et felt med navnet field. Det nye field nøgleord skygger et felt med navnet field i området for en egenskabsadgang. Du kan enten ændre navnet på feltvariablen eller bruge tokenet @ til at referere til felt-id'et som @field.
Delvise metoder gør det muligt for implementereren af en del af en klasse at deklarere et medlem. Implementereren af en anden del af klassen kan definere det pågældende medlem. Der er to scenarier, hvor denne adskillelse er nyttig: skabeloner, der genererer standardkode og kildegeneratorer.
Skabelonkode: Skabelonen reserverer et metodenavn og en signatur, så den genererede kode kan kalde metoden. Disse metoder følger de begrænsninger, der gør det muligt for en udvikler at beslutte, om metoden skal implementeres. Hvis metoden ikke implementeres, fjerner compileren metodesignaturen og alle kald til metoden. Kald til metoden, herunder eventuelle resultater, der ville opstå fra evalueringen af argumenter i kald, har ingen effekt på kørselstidspunktet. Derfor kan enhver kode i den delvise klasse frit bruge en delvis metode, selvom implementeringen ikke er angivet. Der opstår ingen kompileringsfejl eller kørselsfejl, hvis metoden kaldes, men ikke implementeres.
Kildegeneratorer: Kildegeneratorer leverer en implementering for medlemmer. Den menneskelige udvikler kan tilføje medlemserklæringen (ofte med attributter, der læses af kildegeneratoren). Udvikleren kan skrive kode, der kalder disse medlemmer. Kildegeneratoren kører under kompilering og leverer implementeringen. I dette scenarie følges begrænsningerne for delvise medlemmer, der muligvis ikke implementeres, ofte ikke.
// Definition in file1.cs partial void OnNameChanged(); // Implementation in file2.cs partial void OnNameChanged() { // method body }Delvise medlemserklæringer skal begynde med det kontekstafhængige nøgleord delvis.
Delvise medlemssignaturer i begge dele af den delvise type skal stemme overens.
Delvist medlem kan have statiske og usikre modifikatorer.
Delvist medlem kan være generisk. Begrænsninger skal være de samme i definitions- og implementeringsmetodeerklæringen. Parameter- og typeparameternavne behøver ikke at være de samme i implementeringserklæringen som i den definerende.
Du kan gøre en stedfortræder til en delvis metode, der er defineret og implementeret, men ikke til en delvis metode, der ikke har en implementering.