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

Dědičnost, společně s zapouzdřením a polymorfismus, 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 ClassAz ClassB, 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 s názvem Mammal a jinou odvozenou třídu, která má název Reptile. A Mammal je a Reptile je Animal, Animalale každá odvozená třída představuje různé specializace základní třídy.

Deklarace rozhraní mohou definovat výchozí implementaci jejích členů. Tyto implementace jsou zděděny odvozenými rozhraními a třídami, které tyto rozhraní implementují. 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 ho 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 z System.Object a dědí všechny jeho metody. WorkItem přidá šest členů vlastního. Mezi tyto členy patří konstruktor, protože konstruktory nejsou zděděny. Třída ChangeRequest dědí a WorkItem představuje určitý druh pracovní položky. ChangeRequest přidá dva další členy k členům, ze kterého dědí a WorkItem od Object. Musí přidat vlastní konstruktor a také přidá originalItemID. 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 that shows class inheritance

Následující příklad ukazuje, jak se relace tříd demonstrují v předchozím obrázku v jazyce C#. Příklad také ukazuje, jak WorkItem přepíše 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 člen 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 deklarovány jako abstraktní. Tyto podpisy určují parametry a návratnou hodnotu, ale nemají žádnou implementaci (tělo metody). Abstraktní třída nemusí obsahovat abstraktní členy; Pokud však třída obsahuje abstraktní člen, třída sama 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 typ odkazu, 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é z těchto členů nebo pro všechny tyto členy. Třída může implementovat více rozhraní, i když může odvozovat 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í může být například implementováno 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 Mammal ).Animal Další informace najdete v tématu Rozhraní.

Prevence dalšího odvození

Třída může zabránit tomu, aby zdědily z ní jiné třídy nebo z libovolného jeho člena deklarací samotného 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 člena. Použití new není povinné, ale pokud se nepoužívá, vygeneruje new se upozornění kompilátoru. Další informace najdete v tématu Správa verzí s přepsáním a novými klíčovými slovy a vědět, kdy použít přepsání a nová klíčová slova.