Na webu jsou dokumenty různých typů. Pro zjednodušení nás však nyní zajímají jen HTML a RSS dokumenty. Tuto skutečnost popíšeme třemi třídami. Jedna je abstraktní a popisuje to, co je pro HTML a RSS společné, další dvě pak popisují specifické vlastnosti daného dokumentu. Co by přesně popisovaly, není podstatné, jde mi o to nastínit situaci, ve které dává smysl použití dědičnosti.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public abstract class Document {
public Document(Uri url) {
Url = url;
}
public Uri Url { get ; private set ; }
}
public class HtmlDocument : Document {
public HtmlDocument(Uri url) : base (url) { }
}
public class RssDocument : Document {
public RssDocument(Uri url) : base (url) { }
}
|
Všechny dokumenty mají společnou jednu věc – URL adresu – jednoznačný identifikátor na webu. Řekněme, že třídu HtmlDocument používáme ke zpracování nějakých dat a zajímá nás, nebo spíše chceme ovlivnit chování vzhledem k datovým strukturám, jako je List<T>. Vyjděme z výchozího chování.
1 2 3 | var list = new List<Document>();
|
Hodnota proměnné result je false. V programu se vytvoří dvě třídy, dvě odlišné reference a u nich object.ReferenceEquals vrací false.
Když chceme docílit toho, aby se .NET zajímal nejen o shodnost referencí, ale i shodnost dat, implementujeme třídě Document rozhraní IEquatable<T>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public abstract class Document : IEquatable<Document> {
public Document(Uri url) {
Url = url;
}
public Uri Url { get ; private set ; }
public bool Equals(Document other) {
if ( this .Url != null && other.Url != null ) {
return this .Url.Equals(other.Url);
} else {
return base .Equals(other);
}
}
}
|
Potom už se bude List<T> chovat odlišně:
1 2 3 | var list = new List<Document>();
|
Hodnota proměnné result je nyní true. Prima, základní problém je vyřešen. Jenže k praktické použitelnosti má tento kód ještě hodně daleko.
Tak především Document je pouze bázová třída. Po vyjmutí z List<T> chci ale pracovat s třídou HtmlDocument. Přetypování je zdrojem chyb. Chci vlastně List<HtmlDocument>. Na List<Document> jsem slevil jen jako ústupek IEquatable<Document>. Není to špatně?
1 2 3 | var list = new List<HtmlDocument>();
|
Hodnota proměnné result je false, protože .NET nehledá IEquatable<T> v bázových třídách. Tuto dědičnost je třeba doprogramovat:
1 2 3 4 5 6 7 8 9 | public class HtmlDocument : Document, IEquatable<HtmlDocument> {
public HtmlDocument(Uri url) : base (url) { }
public bool Equals(HtmlDocument other) {
return base .Equals(other);
}
}
|
Teď už se vše chová tak, jak očekáváme.
1 2 3 | var list = new List<HtmlDocument>();
|
Hodnota proměnné result je true. Logika porovnávání je v bázové třídě a společná pro všechny potomky.