Dědičnost – odvození typů za účelem vytvoření specializovanějšího chování

Dědičnost, spolu se zapouzdřením a polymorfismu, je jednou ze tří hlavních charakteristik objektově orientovaného programování. Dědičnost umožňuje vytvářet nové třídy, které opakovaně používají, rozšiřují a upravují chování definované v jiných třídách. Třída, jejíž členy jsou zděděny, se nazývá základní třída 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í. Je-li ClassC odvozen z , a ClassB je odvozen z ClassAClassB, ClassC dědí členy deklarované v ClassB a ClassA.

Poznámka:

Struktury nepodporují dědičnost, ale mohou implementovat rozhraní.

Odvozená třída je koncepčně specializace základní třídy. Pokud máte například základní třídu Animal, můžete mít jednu odvozenou třídu, která je pojmenována Mammal , a jinou odvozenou třídu, která je pojmenována Reptile. A Mammal je a ReptileAnimalje Animal, ale každá odvozená třída představuje různé specializace základní třídy.

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

Když definujete třídu, která má být odvozena z jiné třídy, odvozená třída implicitně získá všechny členy základní třídy s výjimkou jejích konstruktorů a finalizátorů. Odvozená třída znovu použije kód v základní třídě, aniž by ji musel znovu vytvořit. Do odvozené třídy můžete přidat další členy. Odvozená třída rozšiřuje funkce základní třídy.

Následující obrázek znázorňuje třídu WorkItem , která představuje položku práce v některém obchodním procesu. Stejně jako všechny třídy je odvozen od System.Object a dědí všechny jeho metody. WorkItem přidá šest členů svého vlastního. Tyto členy zahrnují konstruktor, protože konstruktory nejsou zděděné. Třída ChangeRequest dědí z WorkItem konkrétního typu pracovní položky a představuje konkrétní druh pracovní položky. ChangeRequest přidá dva další členy do členů, které dědí z WorkItem a z Object. Musí přidat vlastní konstruktor a přidá originalItemIDtaké . Vlastnost originalItemID umožňuje ChangeRequest , aby byla instance přidružena k původnímu WorkItem objektu, na který se vztahuje žádost o změnu.

Diagram znázorňující dědičnost tříd

Následující příklad ukazuje, jak jsou relace tříd znázorněné na předchozím obrázku vyjádřeny v jazyce C#. Příklad také ukazuje, jak WorkItem přepisuje virtuální metodu Object.ToStringa 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žívat 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 override metodu s vlastní implementací. Pokud základní třída deklaruje člena jako abstract, musí být tato metoda přepsána v jakékoli ne abstraktní třídě, která přímo dědí z této třídy. Pokud je odvozená třída sama abstraktní, dědí abstraktní členy bez jejich implementace. Abstraktní a virtuální členy jsou základem polymorfismu, což je druhá primární charakteristika objektově orientovaného programování. Další informace naleznete v tématu Polymorfismus.

Abstraktní základní třídy

Třídu můžete deklarovat jako abstraktní, pokud chcete zabránit přímé instanci pomocí nového operátoru. Abstraktní třídu lze použít pouze v případě, že je z ní odvozena nová třída. Abstraktní třída může obsahovat jeden nebo více podpisů metody, které jsou samy deklarovány jako abstraktní. Tyto podpisy určují parametry a návratové hodnoty, ale nemají žádnou implementaci (tělo metody). Abstraktní třída nemusí obsahovat abstraktní členy; Nicméně, pokud třída obsahuje abstraktní člen, třída samotná musí být deklarována jako abstraktní. Odvozené třídy, které nejsou abstraktní samy, musí poskytovat implementaci pro všechny abstraktní metody z abstraktní základní třídy.

Rozhraní

Rozhraní je referenční 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 libovolný nebo všechny tyto členy. Třída může implementovat více rozhraní, i když může odvodit pouze z jedné přímé základní třídy.

Rozhraní se používají k definování konkrétních možností pro třídy, které nemusí nutně mít relaci "is a". Rozhraní lze například implementovat libovolnou třídou nebo strukturou, aby bylo možné určit, System.IEquatable<T> zda jsou dva objekty typu ekvivalentní (ale typ definuje ekvivalenci). IEquatable<T>neznamená, že stejný typ "je" vztah, který existuje mezi základní třídou a odvozenou třídou (například je Animal).Mammal Další informace naleznete v tématu Rozhraní.

Zabránění dalšího odvozování

Třída může zabránit tomu, aby zděděly jiné třídy, nebo z libovolného ze svých členů, tím, že deklaruje sám sebe nebo člena jako sealed.

Odvozená třída skrytí č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 podpisem. new Modifikátor lze použít k explicitní označení, že člen není určen k přepsání základního členu. Použití new není povinné, ale pokud se nepoužívá, vygeneruje new se upozornění kompilátoru. Další informace naleznete v tématu Správa verzí s přepsáním a novými klíčovými slovy a znalost, kdy použít přepsání a nová klíčová slova.