C# týden ITnetwork Flashka zdarma
Akce! Pouze tento týden sleva až 80 % na kurzy C# .NET. Lze kombinovat s akcí 50 % bodů navíc na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

Lekce 14 - Rozhraní (interface)

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Diář s databází v C#, jsme si procvičili práci s kolekcí List při tvorbě elektronického diáře. Dnes to v C# tutoriálu bude opět trochu teoretické, objevíme další taje objektově orientovaného programování, uvedeme si totiž rozhraní.

Rozhraní

Rozhraním objektu se myslí to, jak je objekt viditelný zvenku. Již víme, že objekt obsahuje nějaké metody, ty mohou být privátní nebo veřejné. Rozhraní objektu tvoří právě jeho veřejné metody, je to způsob, jakým s určitým typem objektu můžeme komunikovat. Již jsme několikrát mohli vidět jaké veřejné metody naše třída nabízí, např. u našeho bojovníka do arény. Třída Bojovnik měla následující veřejné metody:

  • void Utoc(Bojovnik souper)
  • void BranSe(int uder)
  • bool Nazivu()
  • void NastavZpravu(string zprava)
  • string VratPosledniZpravu()
  • string GrafickyZivot()

Pokud si do nějaké úproměnné uložíme instanci bojovníka, můžeme na ni volat metody jako Utoc() nebo BranSe(). To pořád není nic nového, že?

My si však rozhraní můžeme deklarovat zvlášť a to podobným způsobem jako např. třídu. Toto rozhraní poté použijeme jako datový typ.

Vše si vyzkoušíme, ale na něčem jednodušším, než je bojovník. Vytvořme si nový projekt, konzolovou aplikaci a nazvěme ho Rozhrani. Přidáme si nějakou jednoduchou třídu. Protože by se dle mého názoru měla teorie vysvětlovat na něčem odlehčujícím, vytvoříme ptáka. Bude umět pípat, dýchat a klovat. Přidejme si třídu Ptak, bude vypadat takto:

class Ptak
{

    public void Pipni()
    {
        Console.WriteLine("♫ ♫ ♫");
    }

    public void Dychej()
    {
        Console.WriteLine("Dýchám...");
    }

    public void Klovni()
    {
        Console.WriteLine("Klov, klov!");
    }

}

Třída je opravdu triviální. Přejděme do Program.cs a vytvořme si instanci ptáka:

Ptak ptak = new Ptak();

Nyní napíšeme ptak. a necháme Visual Studio, aby nám zobrazilo metody na třídě (lze také vyvolat stiskem ctrl + space):

Metody ptáka

Vidíme, co na ptákovi můžeme vše volat. Jsou tam samozřejmě ty 3 metody, co jsme ve třídě implementovali (plus další, které mají objekty v základu).

Nyní ptákovi vytvoříme rozhraní. Využijeme k tomu klíčového slova interface (anglicky rozhraní). Je dobrým zvykem, že rozhraní v C# začíná vždy na písmeno I (jako Interface). Vytvořme tedy interface IPtak. Pravým tlačítkem klikneme na projekt, a přidáme nový item -> Interface.

Nový interface ve Visual Studiu

K projektu se nám přidá prázdný interface. Do něj přidáme hlavičky metod, které má dané rozhraní obsahovat. Samotnou implementaci (kód metod) uvedeme až ve třídě, která bude toto rozhraní implementovat (viz dále).

Do rozhraní IPtak tedy přidáme hlavičky metod, schválně jednu vynecháme a přidáme pouze pípání a dýchání:

interface IPtak
{
    void Pipni();
    void Dychej();
}

Modifikátor public neuvádíme, protože rozhraní obsahuje vždy pouze veřejné metody (jinak by nemělo smysl, udává, jak s objektem zvenku pracovat).

Vraťme se do Program.cs a změňme řádek s proměnnou ptak tak, aby již nebyla typu Ptak, ale IPtak:

IPtak ptak = new Ptak();

Kódem výše říkáme, že v proměnné typu IPtak očekáváme objekt, který obsahuje ty metody, co jsou v rozhraní. Visual Studio nám vyhubuje, protože třída Ptak zatím rozhraní IPtak neobsahuje, i když potřebné metody má, neví, že rozhraní poskytuje. Přesuneme se do třídy Ptak a nastavíme jí, že implementuje interface IPtak. Dělá se to stejně, jako když třída od jiné dědí:

class Ptak: IPtak
{
    // . . .

Když se nyní vrátíme do Program.cs, řádek s proměnnou typu IPtak je již v pořádku. Třída Ptak korektně implementuje rozhraní IPtak a její instance může být do proměnné tohoto typu uložena.

Zkusme nyní vymazat ze třídy nějakou metodu, kterou rozhraní udává, např. Pipni(). Visual Studio nás upozorní, že implementace není kompletní. Vraťme ji zas zpět.

Opět přidáme řádek ptak., Visual Studio nám nabídne následující metody:

Metody ptáka s rozhraním IPtak

Vidíme, že na instanci můžeme nyní volat pouze metody, které poskytuje rozhraní. To proto, že proměnná ptak je již typu IPtak, nikoli Ptak. Metoda Klovni() úplně chybí.

K čemu je to dobré? Výhod a využití je více, na první jsme již přišli. Pomocí rozhraní dokážeme zjednodušit rozhraní nějakého složitého objektu a vystavit jen tu část, která se nám v tu dobu hodí.

Ještě dodám, že nemůžeme vytvořit instanci z rozhraní, tento kód nebude fungovat:

// tento kód nebude fungovat
IPtak ptak = new IPtak();

Vícenásobná dědičnost

C# (stejně jako většina programovacích jazyků) nepodporuje vícenásobnou dědičnost. Nemůžeme tedy jednu třídu oddědit z několika jiných tříd. Je to hlavně z toho důvodu, že může vyvstat problém kolize názvů metod v různých třídách, ze kterých dědíme. Vícenásobná dědičnost se často obchází právě přes interface, protože těch můžeme ve třídě implementovat kolik chceme. Umožňuje nám to s instancí poté pracovat určitým způsobem a vůbec nás nezajímá, jakého typu objekt ve skutečnosti je a co všechno navíc obsahuje.

Přidejme si k projektu interface IJester. Bude to interface ještěra. Ten bude umět také dýchat a ještě se plazit:

interface IJester
{
    void PlazSe();
    void Dychej();
}

Vyzkoušejme si "vícenásobnou dědičnost", přesněji implementaci více rozhraní v jedné třídě. Udělejme si ptakoještěra. Přidejme k projektu třídu PtakoJester.cs. Bude implementovat rozhraní IPtak a IJester:

class PtakoJester: IJester, IPtak
{
}

Když nyní klikneme v tomto kódu pravým tlačítkem na jednotlivé interface (tedy na IJester a IPtak), můžeme v kontextovém menu zvolit možnost Implement Interface. Visual Studio nám poté do třídy automaticky vygeneruje potřebné metody.

Automatická implementace

Po implementaci obou rozhraní vypadá kód třídy takto:

class PtakoJester: IJester, IPtak
{
    public void PlazSe()
    {
        throw new NotImplementedException();
    }

    public void Dychej()
    {
        throw new NotImplementedException();
    }

    public void Pipni()
    {
        throw new NotImplementedException();
    }
}

Metody doimplementujeme:

public void PlazSe()
{
    Console.WriteLine("Plazím se...");
}

public void Dychej()
{
    Console.WriteLine("Dýchám...");
}

public void Pipni()
{
    Console.WriteLine("♫ ♫♫ ♫ ♫ ♫♫");
}

A přesuňme se do Program.cs a vytvořme si instanci ptakoještěra:

PtakoJester ptakojester = new PtakoJester();

Ujistěme se, že má metody jak ptáka, tak ještěra:

Metody ptáka a ještěra

V příští lekci, Přetypování a hierarchie objektů, budeme pokračovat v podobném duchu. Rozhraní ještě neopustíme a naučíme se další pokročilé techniky objektově orientovaného programování.


 

Stáhnout

Staženo 802x (23.49 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
25 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Diář s databází v C#
Všechny články v sekci
Objektově orientované programování v C#
Miniatura
Následující článek
Přetypování a hierarchie objektů
Aktivity (8)

 

 

Komentáře
Zobrazit starší komentáře (55)

Avatar
Ondřej Čech:20.11.2017 17:06

Mně ten right click nezobrazuje takové menu. Asi jinačí verze Visualka. Musím tam přes tu žárovečku, která mi nabídne jak opravit chybu a doimplementuje to. Nebo jsem jen vocas a něco jsem někde přehlédl?

 
Odpovědět  +1 20.11.2017 17:06
Avatar
 
Odpovědět 13.12.2017 15:25
Avatar
Milan
Člen
Avatar
Milan:4.7.2018 20:29

Ahoj, můžu v třídě, ve které je implementovaný interface, respektive metody z interface je udělat nějakým způsobem privátní? Chápu, že interface musí být public, ale proč nemůžu udělat private metodu v třídě? Díky za odpověď.

 
Odpovědět 4.7.2018 20:29
Avatar
Odpovídá na Milan
Michal Šmahel:4.7.2018 20:40

Ahoj, asi jsi správně nepochopil význam rozhraní. Rozhraní se používají pro to, že definují, jak se třída bude chovat navenek. Z toho vyplývá, že definují povinné veřejné metody, skrz ně následně můžeme přistupovat ke každé třídě implementující dané rozhraní. Pro tvé potřeby se raději podívej po abstraktních třídách.

Odpovědět  +1 4.7.2018 20:40
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
Milan
Člen
Avatar
Odpovídá na Michal Šmahel
Milan:4.7.2018 20:45

Díky za odpověď. Já to spíše bral tak, že si udělám seznam metod, které budu potřebovat a implementací interface na ně nezapomenu. Ale v tom případě tomu rozumím, děkuji.

 
Odpovědět 4.7.2018 20:45
Avatar
Odpovídá na Milan
Michal Šmahel:4.7.2018 20:56

Potom ti pomůže abstraktní třída.

Odpovědět 4.7.2018 20:56
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
Michael K.
Člen
Avatar
Michael K.:2. května 10:55

Jedna věc mi není ohledně Interface jasná.

Jaký je rozdíl mezi
IPtak ptak = new Ptak(); a Ptak ptak = new Ptak();

přeci pokud implementuji rozhraní IPtak ve tříde Ptak, tak tam implementovány metody budou.

Nebo jo to ochrana před tím, kdyby třída Ptak rozhraní IPtak neimplementovalo ?

Polopaticky řečeno - tím, že napíšu IPtak ptak = new Ptak(); kompilátoru řeknu, ověř mi, zda třída Ptak implementuje rohzraní IPtak ? Chápu to dobře ?

Jak by to bylo v případě, že bych chtěl zajistit, aby třída Ptak implementovala více rozhraní ? Při vytváření instance si musím vybrat pouze jednu ?

Děkuji

 
Odpovědět 2. května 10:55
Avatar
David Jančík
Tým ITnetwork
Avatar
Odpovídá na Michael K.
David Jančík:2. května 11:30

Tvůj dotaz je v článku zodpovězen:

K čemu je to dobré? Výhod a využití je více, na první jsme již přišli. Pomocí rozhraní dokážeme zjednodušit rozhraní nějakého složitého objektu a vystavit jen tu část, která se nám v tu dobu hodí.

Odpovědět  +2 2. května 11:30
Zapomeň, že je to nemožné a udělej to ;)
Avatar
Michael K.
Člen
Avatar
Michael K.:2. května 11:36

Takže pokud by třída Ptak implementovala další rozhraní (například.:

interface IPridejDoDatabaze { public void PridaniDoDatabaze();}

a já vytvořím instanci IPtak ptak = new Ptak(); tak metodu PirdaniDoDatabaze() neuvidím/nemohu ji použít ? Chápu to dobře?

Editováno 2. května 11:37
 
Odpovědět  +1 2. května 11:36
Avatar
vosa53
Člen
Avatar
Odpovídá na Michael K.
vosa53:2. května 14:12

Ano, pokud ji nepřetypuješ.

IPtak ptak1 = new Ptak();
ptak1.PridaniDoDatabaze(); // Compile time error

Ptak ptak2 = (Ptak)ptak1;
ptak2.PridaniDoDatabaze(); // Lze

IPridejDoDatabaze ptak3 = (IPridejDoDatabaze)ptak1;
ptak3.PridaniDoDatabaze(); // Lze
 
Odpovědět  +1 2. května 14:12
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 65. Zobrazit vše