Genericita, rozhraní a dědičnost

Václav Dajbych       21. 1. 2015       C#, .NET       6323 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

Jak na platby pomocí PayPalu

PayPal je asi nejznámější a celosvětově nepoužívanější řešení pro online platby. V tomto článku si ukážeme, jak používat REST API pro realizaci jednoduché platby.

LINQ a Entity Framework - díl 3.: LINQ - Rozhraní Ienumerable, iqueryable a yield return

Jeden antipattern, který dokáže asynchronní programování pořádně znepříjemnit

 

 

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