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.
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í.
var list = new List<Document>();
list.Add(new HtmlDocument(new Uri("http://dajbych.net")));
var result = list.Contains(new HtmlDocument(new Uri("http://dajbych.net")));
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>
:
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ě:
var list = new List<Document>();
list.Add(new HtmlDocument(new Uri("http://dajbych.net")));
var result = list.Contains(new HtmlDocument(new Uri("http://dajbych.net")));
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ě?
var list = new List<HtmlDocument>();
list.Add(new HtmlDocument(new Uri("http://dajbych.net")));
var result = list.Contains(new HtmlDocument(new Uri("http://dajbych.net")));
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:
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.
var list = new List<HtmlDocument>();
list.Add(new HtmlDocument(new Uri("http://feedviewer.net")));
var result = list.Contains(new HtmlDocument(new Uri("http://feedviewer.net")));
Hodnota proměnné result
je true
. Logika porovnávání je v bázové třídě a společná pro všechny potomky.