Genericita, rozhraní a dědičnost

Václav Dajbych       21.01.2015       C#, .NET       12749 zobrazení

Jazyk C# je multiparadigmatický, což v praxi znamená, že v něm můžeme dělat hodně věcí. Jak ale do sebe jednotlivá paradigma zapadají? Co se hezky doplňuje a co není vzájemně kompatibilní? V tomto článku chci popsat, jak se chová IEquatable<T> vzhledem k dědičnosti typu T.

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://feedviewer.net")));
var result = list.Contains(new HtmlDocument(new Uri("http://feedviewer.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.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Windows 10 GameJam: konference a herní hackathon

Jednoduchý scheduler v .NETu

Asi to znáte – máte nějaký složitější systém na zpracování velkého objemu dat a čas od času potřebujete vykovat nějakou automatizovanou údržbu – typicky smazat všechny položky starší než několika dní. Možností, jak toho dosáhnout, je hodně. Snažil jsem se vymyslet něco jednoduchého a efektivního.

Code First initializers a migrace - kompletní přehled

 

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Fanda

Zajímavé. Díky.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

překlep

Věřím, že jsi chtěl napsat IEquatable<T> v následující větě: "protože .NET nehledá IEnumerable<T> v bázových třídách".

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Opraveno. Díky za upozornění.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback