Dědičnost – odvození typů pro vytvoření více specializovaného chování

Dědičnost, spolu s zapouzdřením a polymorfismus, je jednou ze tří základních vlastností objektově orientovaného programování. Dědičnost umožňuje vytvořit nové třídy, které znovu použije, rozšíří a mění chování definované v jiných třídách. Třída, jejíž členové jsou zděděni, se nazývají základní třídu a třída, která dědí tyto členy, se nazývá odvozená třída. Odvozená třída může mít pouze jednu přímou základní třídu. Dědičnost je však tranzitivní. Pokud ClassC je odvozen z a ClassB ClassB je odvozen z ClassA , ClassC dědí členy deklarované v ClassB a ClassA .

Poznámka

Struktury nepodporují dědění, ale mohou implementovat rozhraní.

V koncepčním případě je odvozená třída specializací základní třídy. Například pokud máte základní třídu Animal , může mít jednu odvozenou třídu s názvem Mammal a jinou odvozenou třídu s názvem Reptile . A je Mammal Animal , a Reptile je Animal , ale každá odvozená třída představuje různé specializace základní třídy.

Deklarace rozhraní mohou definovat výchozí implementaci pro své členy. Tyto implementace jsou zděděny odvozenými rozhraními a třídami, které implementují tato rozhraní. Další informace o metodách výchozích rozhraní najdete v článku o rozhraních.

Při definování třídy pro odvození z jiné třídy odvozená třída implicitně získá všechny členy základní třídy, s výjimkou jejích konstruktorů a finalizační metody. Odvozená třída znovu používá kód v základní třídě, aniž by bylo nutné ho znovu implementovat. V odvozené třídě můžete přidat další členy. Odvozená třída rozšiřuje funkčnost základní třídy.

Následující ilustrace znázorňuje třídu WorkItem , která představuje položku práce v některém obchodním procesu. Podobně jako všechny třídy je odvozen z System.Object a dědí všechny metody. WorkItem Přidá pět členů vlastního typu. Tyto členy zahrnují konstruktor, protože konstruktory nejsou děděny. Třída ChangeRequest dědí z WorkItem a představuje konkrétní typ pracovní položky. ChangeRequest přidá dva členy do členů, které dědí z WorkItem a z Object . Je nutné přidat svůj vlastní konstruktor a přidá také originalItemID . Vlastnost originalItemID umožňuje ChangeRequest instanci přidružit k původnímu, WorkItem na kterou se vztahuje žádost o změnu.

Diagram, který znázorňuje dědičnost tříd

Následující příklad ukazuje, jak jsou vztahy tříd znázorněny na předchozím obrázku, vyjádřeny v jazyce C#. Příklad také ukazuje, jak WorkItem Přepisuje virtuální metodu Object.ToString a jak ChangeRequest Třída dědí WorkItem implementaci metody. První blok definuje třídy:

// 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;
    }
}

Tento další blok ukazuje, jak použít základní a odvozené třídy:

// 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
*/

Abstraktní a virtuální metody

Když základní třída deklaruje metodu jako virtual , odvozená třída může být override metodou s vlastní implementací. Pokud základní třída deklaruje člena jako abstract , musí být tato metoda přepsána v jakékoli neabstraktní třídě, která přímo dědí z této třídy. Pokud je odvozená třída sama o sobě abstraktní, dědí abstraktní členy bez jejich implementace. Abstraktní a virtuální členové jsou základem pro polymorfismus, což je druhá primární charakteristika objektově orientovaného programování. Další informace najdete v tématu polymorfismus.

Abstraktní základní třídy

Třídu můžete deklarovat jako abstraktní , pokud chcete zabránit přímému vytváření instancí pomocí operátoru New . Abstraktní třída se dá použít jenom v případě, že je z ní odvozená nová třída. Abstraktní třída může obsahovat jeden nebo více signatur metod, které jsou deklarovány jako abstraktní. Tyto signatury určují parametry a návratovou hodnotu, ale nemají žádnou implementaci (tělo metody). Abstraktní třída nemusí obsahovat abstraktní členy; Pokud však třída obsahuje abstraktní člen, musí být samotná třída deklarována jako abstraktní. Odvozené třídy, které nejsou abstraktní, musí poskytovat implementaci pro všechny abstraktní metody z abstraktní základní třídy.

Rozhraní

Rozhraní je odkazový typ, který definuje sadu členů. Všechny třídy a struktury, které implementují toto rozhraní, musí implementovat tuto sadu členů. Rozhraní může definovat výchozí implementaci pro některé nebo všechny tyto členy. Třída může implementovat více rozhraní, i když může odvozovat pouze z jediné přímé základní třídy.

Rozhraní slouží k definování specifických možností pro třídy, které nemají nutně mít relaci "je". System.IEquatable<T>Rozhraní může být například implementováno jakoukoliv třídou nebo strukturou pro určení, zda jsou dva objekty typu ekvivalent (ale typ definuje ekvivalenci). IEquatable<T> neznamená, že stejný druh vztahu "je", který existuje mezi základní třídou a odvozenou třídou (například Mammal je a Animal ). Další informace naleznete v tématu rozhraní.

Zabránit dalšímu odvození

Třída může zabránit jiným třídám dědit z ní nebo z některého z jejích členů deklarací samotného nebo členu jako sealed .

Odvozená třída skrývání členů základní třídy

Odvozená třída může skrýt členy základní třídy deklarováním členů se stejným názvem a signaturou. Pomocí new modifikátoru lze explicitně označit, že člen není určen jako přepsání základního člena. Použití new není vyžadováno, ale pokud se nepoužije, bude vygenerováno upozornění kompilátoru new . Další informace naleznete v tématu Správa verzí pomocí klíčových slov override a New a znalost, kdy použít klíčová slova override a New.