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 ClassA
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, která je pojmenována Mammal
, a jinou odvozenou třídu, která je pojmenována Reptile
. A Mammal
je a Reptile
Animal
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 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á originalItemID
také . 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.
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.