Share via


Arv – härled typer för att skapa mer specialiserade beteenden

Arv, tillsammans med inkapsling och polymorfism, är en av de tre främsta egenskaperna för objektorienterad programmering. Med arv kan du skapa nya klasser som återanvänder, utökar och ändrar beteendet som definieras i andra klasser. Klassen vars medlemmar ärvs kallas för basklassen och klassen som ärver dessa medlemmar kallas för den härledda klassen. En härledd klass kan bara ha en direkt basklass. Arv är dock transitivt. Om ClassC härleds från ClassB, och ClassB härleds från ClassA, ClassC ärver de medlemmar som deklareras i ClassB och ClassA.

Kommentar

Structs stöder inte arv, men de kan implementera gränssnitt.

Konceptuellt är en härledd klass en specialisering av basklassen. Om du till exempel har en basklass Animalkan du ha en härledd klass med namnet Mammal och en annan härledd klass med namnet Reptile. A Mammal är en Animal, och en Reptile är en Animal, men varje härledd klass representerar olika specialiseringar för basklassen.

Gränssnittsdeklarationer kan definiera en standardimplementering för dess medlemmar. Dessa implementeringar ärvs av härledda gränssnitt och av klasser som implementerar dessa gränssnitt. Mer information om standardgränssnittsmetoder finns i artikeln om gränssnitt.

När du definierar en klass som ska härledas från en annan klass får den härledda klassen implicit alla medlemmar i basklassen, förutom dess konstruktorer och finalizers. Den härledda klassen återanvänder koden i basklassen utan att behöva omimplementeras. Du kan lägga till fler medlemmar i den härledda klassen. Den härledda klassen utökar funktionerna i basklassen.

Följande bild visar en klass WorkItem som representerar ett arbetsobjekt i en affärsprocess. Precis som alla klasser härleds den från System.Object och ärver alla sina metoder. WorkItem lägger till sex egna medlemmar. Dessa medlemmar inkluderar en konstruktor eftersom konstruktorer inte ärvs. Klassen ChangeRequest ärver från WorkItem och representerar en viss typ av arbetsobjekt. ChangeRequest lägger till ytterligare två medlemmar till de medlemmar som ärver från WorkItem och från Object. Den måste lägga till en egen konstruktor och lägger även till originalItemID. Egenskapen originalItemID gör att instansen ChangeRequest kan associeras med originalet WorkItem som ändringsbegäran gäller för.

Diagram som visar klassarv

I följande exempel visas hur klassrelationerna som visades i föregående bild uttrycks i C#. Exemplet visar också hur WorkItem åsidosätter den virtuella metoden Object.ToStringoch hur ChangeRequest klassen ärver implementeringen WorkItem av metoden. Det första blocket definierar klasserna:

// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
    // Static field currentID stores the job ID of the last WorkItem that
    // has been created.
    private static int currentID;

    //Properties.
    protected int ID { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    protected TimeSpan jobLength { get; set; }

    // Default constructor. If a derived class does not invoke a base-
    // class constructor explicitly, the default constructor is called
    // implicitly.
    public WorkItem()
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }

    // Instance constructor that has three parameters.
    public WorkItem(string title, string desc, TimeSpan joblen)
    {
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = joblen;
    }

    // Static constructor to initialize the static member, currentID. This
    // constructor is called one time, automatically, before any instance
    // of WorkItem or ChangeRequest is created, or currentID is referenced.
    static WorkItem() => currentID = 0;

    // currentID is a static field. It is incremented each time a new
    // instance of WorkItem is created.
    protected int GetNextID() => ++currentID;

    // Method Update enables you to update the title and job length of an
    // existing WorkItem object.
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override of the ToString method that is inherited
    // from System.Object.
    public override string ToString() =>
        $"{this.ID} - {this.Title}";
}

// ChangeRequest derives from WorkItem and adds a property (originalItemID)
// and two constructors.
public class ChangeRequest : WorkItem
{
    protected int originalItemID { get; set; }

    // Constructors. Because neither constructor calls a base-class
    // constructor explicitly, the default constructor in the base class
    // is called implicitly. The base class must contain a default
    // constructor.

    // Default constructor for the derived class.
    public ChangeRequest() { }

    // Instance constructor that has four parameters.
    public ChangeRequest(string title, string desc, TimeSpan jobLen,
                         int originalID)
    {
        // The following properties and the GetNexID method are inherited
        // from WorkItem.
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;

        // Property originalItemID is a member of ChangeRequest, but not
        // of WorkItem.
        this.originalItemID = originalID;
    }
}

Nästa block visar hur du använder basklasserna och härledda klasser:

// Create an instance of WorkItem by using the constructor in the
// base class that takes three arguments.
WorkItem item = new WorkItem("Fix Bugs",
                            "Fix all bugs in my code branch",
                            new TimeSpan(3, 4, 0, 0));

// Create an instance of ChangeRequest by using the constructor in
// the derived class that takes four arguments.
ChangeRequest change = new ChangeRequest("Change Base Class Design",
                                        "Add members to the class",
                                        new TimeSpan(4, 0, 0),
                                        1);

// Use the ToString method defined in WorkItem.
Console.WriteLine(item.ToString());

// Use the inherited Update method to change the title of the
// ChangeRequest object.
change.Update("Change the Design of the Base Class",
    new TimeSpan(4, 0, 0));

// ChangeRequest inherits WorkItem's override of ToString.
Console.WriteLine(change.ToString());
/* Output:
    1 - Fix Bugs
    2 - Change the Design of the Base Class
*/

Abstrakta och virtuella metoder

När en basklass deklarerar en metod som virtualkan override en härledd klass metoden med sin egen implementering. Om en basklass deklarerar en medlem som abstractmåste den metoden åsidosättas i alla icke-abstrakta klasser som ärver direkt från den klassen. Om en härledd klass är abstrakt ärver den abstrakta medlemmar utan att implementera dem. Abstrakta och virtuella medlemmar är grunden för polymorfism, vilket är den andra primära egenskapen för objektorienterad programmering. Mer information finns i Polymorfism.

Abstrakta basklasser

Du kan deklarera en klass som abstrakt om du vill förhindra direkt instansiering med hjälp av den nya operatorn. En abstrakt klass kan endast användas om en ny klass härleds från den. En abstrakt klass kan innehålla en eller flera metodsignaturer som själva deklareras som abstrakta. Dessa signaturer anger parametrarna och returvärdet men har ingen implementering (metodtext). En abstrakt klass behöver inte innehålla abstrakta medlemmar. Men om en klass innehåller en abstrakt medlem måste själva klassen deklareras som abstrakt. Härledda klasser som inte är abstrakta själva måste tillhandahålla implementeringen för abstrakta metoder från en abstrakt basklass.

Gränssnitt

Ett gränssnitt är en referenstyp som definierar en uppsättning medlemmar. Alla klasser och strukturer som implementerar gränssnittet måste implementera den uppsättningen medlemmar. Ett gränssnitt kan definiera en standardimplementering för alla dessa medlemmar. En klass kan implementera flera gränssnitt även om den bara kan härledas från en enda direkt basklass.

Gränssnitt används för att definiera specifika funktioner för klasser som inte nödvändigtvis har en "är en"-relation. Gränssnittet kan till exempel System.IEquatable<T> implementeras av vilken klass eller struct som helst för att avgöra om två objekt av typen är likvärdiga (men typen definierar likvärdighet). IEquatable<T> innebär inte samma typ av "är en"-relation som finns mellan en basklass och en härledd klass (till exempel en Mammal är en Animal). Mer information finns i Gränssnitt.

Förhindra ytterligare härledning

En klass kan förhindra att andra klasser ärver från den, eller från någon av dess medlemmar, genom att deklarera sig själv eller medlemmen som sealed.

Härledd klass som döljer basklassmedlemmar

En härledd klass kan dölja basklassmedlemmar genom att deklarera medlemmar med samma namn och signatur. Modifieraren new kan användas för att uttryckligen ange att medlemmen inte är avsedd att vara en åsidosättning av basmedlemmen. Användning av new krävs inte, men en kompilatorvarning genereras om new den inte används. Mer information finns i Versionshantering med åsidosättning och nya nyckelord och Veta när du ska använda åsidosättning och nya nyckelord.