rozdzia‡ 12 dziedziczenie 189 dziedziczenie method. co wa¿ne, metodamethod deklaruje swój...

21
Dziedziczenie Rozdzia³ 12 Dziedziczenie Czêœæ II Zrozumieæ jêzyk C# W tym rozdziale dowiesz siê, jak: n Utworzyæ klasê pochodn¹, która dziedziczy po klasie bazowej. n Wywo³aæ konstruktor klasy bazowej z konstruktora klasy pochodnej. n Sterowaæ ukrywaniem i przes³anianiem metod za pomoc¹ s³ów kluczowych new, virtual i override. n Ograniczaæ dostêpnoœæ klasy w hierarchii dziedziczenia za pomoc¹ s³owa kluczo- wego protected. n Tworzyæ interfejs zawieraj¹cy tylko nazwy metod. n Implementowaæ interfejs w strukturze lub klasie, pisz¹c treœæ metod. n Zawrzeæ wspólne funkcje implementacji w klasie abstrakcyjnej. n Zadeklarowaæ, ¿e z klasy nie mo¿na wywodziæ nowych klas, za pomoc¹ s³owa klu- czowego sealed. n Napisaæ klasê implementuj¹c¹ wiele interfejsów. n Zrealizowaæ metody interfejsu, u¿ywaj¹c techniki jawnej implementacji interfejsu. Co to jest dziedziczenie? Ogólnie rzecz bior¹c, programiœci czêsto nie rozumiej¹, czym jest dziedziczenie. Po czêœci bierze siê to st¹d, ¿e samo s³owo „dziedziczenie” ma wiele znaczeñ. Jeœli ktoœ zapisuje ci coœ w testamencie, mówimy, ¿e to dziedziczysz. Mówimy te¿, ¿e dziedziczysz po³owê genów po matce, a po³owê po ojcu. Oba te znaczenia nie maj¹ nic wspólnego z dziedzi- czeniem w programowaniu. Tutaj chodzi przede wszystkim o klasyfikacjê, o zwi¹zek miêdzy klasami. Kiedy by³eœ w szkole, zapewne uczy³eœ siê o ssakach i dowiedzia³eœ siê, ¿e pies jest ssakiem. W Visual C# móg³byœ to zobrazowaæ, tworz¹c dwie klasy – jedn¹ o nazwie ssak, drug¹ o nazwie pies – i deklaruj¹c, ¿e pies dziedziczy po ssaku. Dziedziczenie wskazywa³oby, ¿e miêdzy tymi klasami istnieje zwi¹zek, i podkre- œla³o, ¿e wszystkie psy s¹ ssakami. Wa¿ne Dziedziczenie to relacja miêdzy klasami, a nie obiektami. Deklaruj¹c istnie- nie zwi¹zku na poziomie klasy, gwarantujemy, ¿e zachodzi on miêdzy wszystkimi obiektami klasy. Dziedziczenie jest ma³o elastyczne. Szacunkowy czas 60 min. Rozdzia³ 12

Upload: buidien

Post on 15-Jul-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

Dziedziczenie

Rozdzia³ 12 Dziedziczenie

Czêœæ II Zrozumieæ jêzyk C#

W tym rozdziale dowiesz siê, jak:

n Utworzyæ klasê pochodn¹, która dziedziczy po klasie bazowej.

n Wywo³aæ konstruktor klasy bazowej z konstruktora klasy pochodnej.

n Sterowaæ ukrywaniem i przes³anianiem metod za pomoc¹ s³ów kluczowych new,virtual i override.

n Ograniczaæ dostêpnoœæ klasy w hierarchii dziedziczenia za pomoc¹ s³owa kluczo-wego protected.

n Tworzyæ interfejs zawieraj¹cy tylko nazwy metod.

n Implementowaæ interfejs w strukturze lub klasie, pisz¹c treœæ metod.

n Zawrzeæ wspólne funkcje implementacji w klasie abstrakcyjnej.

n Zadeklarowaæ, ¿e z klasy nie mo¿na wywodziæ nowych klas, za pomoc¹ s³owa klu-czowego sealed.

n Napisaæ klasê implementuj¹c¹ wiele interfejsów.n Zrealizowaæ metody interfejsu, u¿ywaj¹c techniki jawnej implementacji interfejsu.

Co to jest dziedziczenie?Ogólnie rzecz bior¹c, programiœci czêsto nie rozumiej¹, czym jest dziedziczenie. Po czêœcibierze siê to st¹d, ¿e samo s³owo „dziedziczenie” ma wiele znaczeñ. Jeœli ktoœ zapisuje cicoœ w testamencie, mówimy, ¿e to dziedziczysz. Mówimy te¿, ¿e dziedziczysz po³owêgenów po matce, a po³owê po ojcu. Oba te znaczenia nie maj¹ nic wspólnego z dziedzi-czeniem w programowaniu. Tutaj chodzi przede wszystkim o klasyfikacjê, o zwi¹zekmiêdzy klasami. Kiedy by³eœ w szkole, zapewne uczy³eœ siê o ssakach i dowiedzia³eœsiê, ¿e pies jest ssakiem. W Visual C# móg³byœ to zobrazowaæ, tworz¹c dwie klasy –jedn¹ o nazwie ssak, drug¹ o nazwie pies – i deklaruj¹c, ¿e pies dziedziczy po ssaku.Dziedziczenie wskazywa³oby, ¿e miêdzy tymi klasami istnieje zwi¹zek, i podkre-œla³o, ¿e wszystkie psy s¹ ssakami.

Wa¿ne Dziedziczenie to relacja miêdzy klasami, a nie obiektami. Deklaruj¹c istnie-nie zwi¹zku na poziomie klasy, gwarantujemy, ¿e zachodzi on miêdzy wszystkimiobiektami klasy. Dziedziczenie jest ma³o elastyczne.

Rozdzia³ 12 Dziedziczenie189

Szacunkowy czas60 min.

Rozdzia³ 12

Podstawowa sk³adniaW tym podrozdziale omówimy podstawow¹ sk³adniê zwi¹zan¹ z dziedziczeniem,któr¹ musisz znaæ, ¿eby tworzyæ klasy dziedzicz¹ce po innych klasach. Poznawszy têsk³adniê bêdziesz móg³ j¹ rozpoznaæ, kiedy zobaczysz j¹ w kodzie lub dokumentacji.

Klasy bazowe i pochodneAby zadeklarowaæ, ¿e klasa dziedziczy po innej klasie, u¿ywamy nastêpuj¹cejsk³adni:

class KlasaPochodna : KlasaBazowa

(...

}

Klasa pochodna dziedziczy po klasie bazowej. Oznacza to, ¿e klasa pochodna jestklas¹ bazow¹. Klasa mo¿e pochodziæ od najwy¿ej jednej klasy. Innymi s³owy, nigdynie pochodzi od dwóch lub wielu klas.

Wskazówka Programuj¹cy w C++ powinni zauwa¿yæ, ¿e nie mo¿na jawnie okre-œliæ, czy dziedziczenie jest publiczne, prywatne czy chronione. Dziedziczenie w C#jest zawsze publiczne. Programuj¹cy w Javie powinni zwróciæ uwagê na brak s³owakluczowego w rodzaju extends.

Klasa System.Object to podstawa wszystkich innych klas. Innymi s³owy, wszystkieklasy niejawnie dziedzicz¹ po klasie System.Object. Jeœli zadeklarujesz klasê w takisposób:

class Token{

public Token(string name){

...}...

}

kompilator przepisze j¹ tak (móg³byœ napisaæ to jawnie, ale lepiej tego nie robiæ):

class Token : System.Object{

public Token(string name){

...}...

}

190 Czêœæ II Zrozumieæ jêzyk C#

Wywo³ywanie konstruktorów klasy bazowejKonstruktor klasy pochodnej musi wywo³ywaæ konstruktor klasy bazowej. W tymcelu u¿ywa siê s³owa kluczowego base. Nie ma tu ¿adnej niejednoznacznoœci, ponie-wa¿ klasa mo¿e mieæ najwy¿ej jedn¹ klasê bazow¹. Oto przyk³ad:

class IdentifierToken : Token{

public IdentifierToken(string name): base(name) // wywo³uje Token(name)

{...

}...

}

Jeœli nie wywo³asz jawnie konstruktora klasy bazowej w konstruktorze klasy pochod-nej, kompilator spróbuje po cichu wstawiæ wywo³anie domyœlnego konstruktora kla-sy bazowej. W poprzednim przyk³adzie kompilator przepisa³by poni¿szy fragment:

class Token{

public Token(string name){

...}...

}

w nastêpuj¹cy sposób:

class Token : System.Object{

public Token(string name): base()

{...

}...

}

To dzia³a, poniewa¿ System.Object ma publiczny konstruktor domyœlny. Jednak¿e niewszystkie klasy go maj¹, a wówczas brak wywo³ania konstruktora klasy bazowej spo-woduje b³¹d kompilacji:

class IdentifierToken : Token{

public IdentifierToken(string name)// b³¹d; klasa bazowa Token nie ma// publicznego konstruktora domyœlnego

{...

}...

}

Rozdzia³ 12 Dziedziczenie 191

Metody newJe¿eli klasa bazowa i pochodna deklaruj¹ dwie metody o takiej samej sygnaturze (sy-gnatur¹ metody jest jej nazwa oraz liczba i typ parametrów), to metody te nie s¹ zesob¹ zwi¹zane. Jeœli spróbujesz skompilowaæ poni¿szy przyk³ad, kompilator wyge-neruje komunikat ostrzegawczy, informuj¹c, ¿e IdentifierToken.Name() ukrywa To-ken.Name():

class Token{

...public string Name() { ... }

}class IdentifierToken : Token{

...public string Name() { ... }

}

Komunikat przypomina, ¿e ze wzglêdu na tak¹ sam¹ sygnaturê i brak zwi¹zku miê-dzy dwoma metodami definicja metody Name w klasie IdentifierToken ukrywa metodêName w klasie Token. Z regu³y tego rodzaju zbie¿noœæ jest (w najlepszym razie)Ÿród³em nieporozumieñ, wiêc powinieneœ zmieniæ nazwy metod, aby unikn¹æ kon-fliktu. Jeœli jednak jesteœ pewien, ¿e dwie metody powinny mieæ tê sam¹ sygnaturê,mo¿esz wyeliminowaæ komunikat ostrzegawczy, u¿ywaj¹c s³owa kluczowego new:

class Token{

...public string Name() { ... }

}class IdentifierToken : Token{

...new public string Name() { ... }

}

U¿ycie s³owa kluczowego new w niczym nie zmienia faktu, ¿e metody nie s¹ ze sob¹zwi¹zane i ¿e jedna ukrywa drug¹. Wy³¹cza tylko komunikat ostrzegawczy. W rezul-tacie s³owo kluczowe new informuje: „Jestem profesjonalnym programist¹ i wiem, corobiê”.

Aby zrozumieæ, na czym polega ukrywanie, rozwa¿ poni¿szy przyk³ad i zastanówsiê, która metoda Name zostanie wywo³ana:

static void Method(Token t){

Console.WriteLine(t.Name());}static void Main(){

IdentifierToken variable = new IdentifierToken("variable");Method(variable);

}

W tym przyk³adzie metoda Main deklaruje zmienn¹ typu IdentifierToken i inicjuje j¹obiektem typu IdentifierToken. Zmienna jest nastêpnie przekazywana jako argument

192 Czêœæ II Zrozumieæ jêzyk C#

metodzie Method. Co wa¿ne, metoda Method deklaruje swój parametr jako Token, a nieIdentifierToken. Jest to dozwolone, poniewa¿ ka¿dy IdentifierToken to Token i mo¿napodstawiæ go za Token. Metoda Method wywo³uje nastêpnie metodê Name tego para-metru. Jako istoty ludzkie mo¿emy spojrzeæ na kod i stwierdziæ, ¿e w tym konkret-nym przypadku parametr – choæ jego typ okreœlono jako Token – w rzeczywistoœciodnosi siê do obiektu typu IdentifierToken. Mo¿na by siê wiêc spodziewaæ, ¿ewywo³anie metody Name oznacza wywo³anie IdentifierToken.Name. Tak jednak niejest: oznacza wywo³anie Token.Name. Prawdopodobnie nie tego sobie ¿yczysz. Naszczêœcie mo¿na sprawiæ, ¿e to wywo³anie bêdzie dzia³a³o zgodnie z oczekiwaniami.

Metody wirtualneAby wywo³anie dzia³a³o jak nale¿y, musisz u¿yæ polimorfizmu. Polimorfizm dos³ownieoznacza „wielokszta³tnoœæ”. W programowaniu tym terminem okreœlamy mo¿liwoœæzaimplementowania tej samej metody wiêcej ni¿ jeden raz. Dziêki polimorfizmowimo¿esz po³¹czyæ dotychczas niezwi¹zane metody Name klasy bazowej i pochodnejoraz zadeklarowaæ, ¿e s¹ dwoma implementacjami tej samej metody. Aby zmieniæmetodê w polimorficzn¹, musisz u¿yæ s³owa kluczowego virtual. Na przyk³ad:

class Token{

...public virtual string Name() { ... }

}

S³owo kluczowe virtual oznacza, ¿e jest to pierwsza implementacja metody o nazwieName. Natomiast brak tego s³owa kluczowego (jak w poprzednim przyk³adzie) ozna-cza, ¿e jest to jedyna implementacja metody o nazwie Name.

Domyœlnie metody C# nie s¹ wirtualne (tak jak w C++, ale inaczej ni¿ w Javie, gdziemetody domyœlnie s¹ wirtualne).

Metody przes³aniaj¹ceJeœli klasa bazowa deklaruje, ¿e metoda jest wirtualna, klasa pochodna mo¿e u¿yæs³owa kluczowego override, aby zadeklarowaæ inn¹ implementacjê tej metody. Naprzyk³ad:

class IdentifierToken : Token{

...public override string Name() { ... }

}

Deklaruj¹c metody polimorficzne za pomoc¹ s³ów kluczowych virtual i override, mu-sisz przestrzegaæ kilku wa¿nych regu³:

n Nie mo¿esz zadeklarowaæ metody prywatnej ze s³owem kluczowym virtual luboverride. Jeœli to zrobisz, spowodujesz b³¹d kompilacji. Prywatne naprawdê znaczyprywatne.

n Obie metody musz¹ byæ identyczne, tzn. mieæ tê sam¹ nazwê, te same typy parame-trów i ten sam typ zwrotny (C# nie obs³uguje kowariancji typów zwrotnych).

Rozdzia³ 12 Dziedziczenie 193

n Dwie metody musz¹ mieæ tê sam¹ dostêpnoœæ. Jeœli jedna metoda jest publiczna,druga tak¿e musi byæ publiczna. (Programiœci C++ powinni zwróciæ na to uwagê.W C++ metody mog¹ mieæ ró¿n¹ dostêpnoœæ).

n Mo¿esz przes³oniæ tylko metodê wirtualn¹. Jeœli metoda klasy bazowej nie jest wir-tualna i spróbujesz j¹ przes³oniæ, spowodujesz b³¹d kompilacji. Tak byæ powinno;to klasa bazowa powinna decydowaæ, czy jej metody mog¹ byæ przes³aniane.

n Jeœli klasa pochodna nie deklaruje metody z wykorzystaniem s³owa kluczowegooverride, to nie przes³ania metody klasy bazowej. Innymi s³owy, staje siê implemen-tacj¹ zupe³nie innej metody, która przypadkiem ma tê sam¹ nazwê. Tak jak po-przednio, spowoduje to ostrze¿enie podczas kompilacji, które mo¿esz wy³¹czyæopisanym ju¿ s³owem kluczowym new.

n Metoda przes³aniaj¹ca jest niejawnie wirtualna i sama mo¿e zostaæ przes³oniêtaw kolejnej klasie pochodnej. Nie mo¿esz tego jednak jawnie deklarowaæ za pomoc¹s³owa kluczowego virtual. Chodzi o to, ¿e metoda nie ma ¿adnych kwalifikatorów,jeœli jest zwyk³¹, niepolimorficzn¹ i nieprzes³aniaj¹c¹ metod¹; w przeciwnym raziema tylko jeden kwalifikator (ta regu³a obowi¹zuje niemal zawsze).

Po zadeklarowaniu metody w klasie bazowej jako wirtualnej, a metody w klasie po-chodnej jako przes³aniaj¹cej, mo¿emy ponownie zaj¹æ siê poprzednim przyk³adem:

static void Method(Token t){

Console.WriteLine(t.Name());}static void Main(){

IdentifierToken variable = new IdentifierToken("variable");Method(variable);

}

Tym razem, kiedy kompilator kompiluje wywo³anie metody Name na parametrze t(zadeklarowanym jako Token), zauwa¿a, ¿e metoda Token.Name jest wirtualna. Z tegowzglêdu nie wywo³uje bezpoœrednio Token.Name (co robi³ poprzednio), ale „najbar-dziej pochodn¹” implementacjê metody Token.Name w rzeczywistym obiekcie, doktórego odnosi siê t. W tym przypadku t odnosi siê do obiektu klasy IdentifierToken,która przes³ania metodê Token.Name; zapis t.Name spowoduje wiêc, zgodnie z oczeki-waniami, wywo³anie metody IdentifierToken.Name.

Dostêp chronionyS³owa kluczowe public i private definiuj¹ skrajne rodzaje dostêpnoœci: sk³adowe pu-bliczne klasy s¹ dostêpne dla ka¿dego, a sk³adowe prywatne – dla nikogo (oczywiœ-cie, z wyj¹tkiem samej klasy). Te dwie skrajnoœci wystarczaj¹, kiedy klasy pozostaj¹w izolacji. Jak jednak wiedz¹ wszyscy doœwiadczeni programiœci jêzyków obiekto-wych, izolowane klasy nie mog¹ rozwi¹zywaæ skomplikowanych problemów. Sekretkryje siê w umiejêtnoœci ³¹czenia klas ze sob¹, w p³ynnym przejœciu od programowa-nia na ma³¹ skalê do programowania na du¿¹ skalê. Dziedziczenie jest potê¿nym me-chanizmem ³¹czenia klas, a miêdzy klas¹ pochodn¹ i jej klas¹ bazow¹ (ale nie

194 Czêœæ II Zrozumieæ jêzyk C#

odwrotnie) zachodzi szczególny, œcis³y zwi¹zek. Odzwierciedla go s³owo kluczoweprotected (chroniony):

n Klasa pochodna ma dostêp do chronionych sk³adowych klasy bazowej. Innymis³owy, wewn¹trz klasy pochodnej chronione sk³adowe klasy bazowej s¹ publiczne.

n Jeœli klasa nie jest klas¹ pochodn¹, nie ma dostêpu do chronionych sk³adowych kla-sy. Innymi s³owy, wewn¹trz klasy nie bêd¹cej klas¹ pochodn¹ chronione sk³adoweinnej klasy s¹ prywatne.

C# daje programistom pe³n¹ swobodê deklarowania metod i pól jako chronionych.Jednak¿e wiêkszoœæ podrêczników programowania obiektowego zaleca, ¿eby polaby³y œciœle prywatne. Pola publiczne naruszaj¹ hermetycznoœæ, poniewa¿ daj¹wszystkim u¿ytkownikom klasy bezpoœredni, niekontrolowany dostêp do pól. Polachronione s¹ hermetyczne dla u¿ytkowników klasy, poniewa¿ s¹ dla nich niedostêp-ne. Pozwalaj¹ jednak na naruszenie hermetycznoœci przez klasy pochodne.

Wskazówka Dostêp do chronionej sk³adowej klasy bazowej mo¿esz uzyskaæ nietylko w klasie pochodnej, ale tak¿e w klasie wywiedzionej z klasy pochodnej. Chro-niona sk³adowa klasy bazowej pozostaje chroniona w klasie pochodnej i w klasachwywiedzionych z klasy pochodnej.

Tworzenie interfejsówDziedziczenie po klasie jest przydatne, ale znacznie wa¿niejsze jest dziedziczenie pointerfejsach. Interfejsy to rzeczywisty powód, dla którego dziedziczenie istnieje. In-terfejsy s¹ bardzo wa¿ne; pozwalaj¹ ca³kowicie oddzieliæ nazwê metody od jej imple-mentacji. Do tej pory nie mogliœmy tego zrobiæ (metoda wirtualna musi mieæ treœæ).Takie oddzielenie daje ogromne mo¿liwoœci.

Interfejsy œciœle oddzielaj¹ „co” od „jak”. Interfejs okreœla tylko nazwê metody, nietroszcz¹c siê o sposób jej realizacji. Interfejs informuje, jak obiekt powinien byæ u¿ywa-ny, a nie jak jest zaimplementowany w danej chwili. Bardzo podobnie funkcjonuj¹ lu-dzie; codziennie u¿ywamy wielu urz¹dzeñ, nie zastanawiaj¹c siê, jak dzia³aj¹. Naprzyk³ad sposób dzia³ania telefonów jest dla ciebie nieistotny, poniewa¿ nie wytwa-rzasz ich, tylko u¿ywasz. Wystarczy, ¿e wiesz, do czego s³u¿y telefon i jak sprawiæ, ¿ebêdzie on pe³ni³ swoj¹ funkcjê. Wystarczy, ¿e znasz interfejs.

Sk³adniaAby zadeklarowaæ interfejs, u¿ywasz s³owa kluczowego interface zamiast class lubstruct. W interfejsie deklarujesz metody tak samo jak w klasie lub strukturze, ale nig-dy nie u¿ywasz modyfikatora dostêpnoœci (public, private, protected), a treœæ metodyzastêpujesz œrednikiem. Na przyk³ad:

interface IToken{

string Name();}

Rozdzia³ 12 Dziedziczenie 195

Wskazówka Dokumentacja Microsoft .NET Framework zaleca, aby nazwy inter-fejsów poprzedzaæ du¿¹ liter¹ I. To ostatni bastion notacji wêgierskiej w C#.

OgraniczeniaNale¿y zapamiêtaæ, ¿e interfejs nigdy nie zawiera implementacji. Nic. Nul. Zero.Ograniczenia s¹ naturaln¹ konsekwencj¹ tego faktu:

n Nie mo¿esz tworzyæ instancji interfejsu. Gdyby to by³o mo¿liwe, co oznacza³obywywo³anie jednej z jego metod, które – z definicji – s¹ tylko nazwane, a nie zaimple-mentowane?

n W interfejsie nie mo¿e byæ ¿adnych pól. Pole jest implementacj¹ atrybutu obiektu.Pola s¹ niedopuszczalne, nawet pola statyczne.

n W interfejsie nie mo¿e byæ konstruktorów. Konstruktor zawiera instrukcje s³u¿¹cedo inicjowania nowo utworzonej instancji obiektu, a nie wolno tworzyæ instancji in-terfejsu.

n W interfejsie nie mo¿e byæ destruktora. Destruktor zawiera instrukcje s³u¿¹ce doniszczenia instancji obiektu, ale ¿eby zniszczyæ obiekt, musisz najpierw go utwo-rzyæ, czego nie mo¿esz zrobiæ – bo to interfejs.

n Nie mo¿esz u¿ywaæ modyfikatora dostêpnoœci. Wszystkie metody interfejsu s¹z za³o¿enia publiczne. Interfejs reprezentuje sposób u¿ycia.

n Nie mo¿esz zagnie¿d¿aæ ¿adnych typów (wyliczeñ, struktur, klas, interfejsów lubdelegacji) w interfejsie.

n Interfejs nie mo¿e pochodziæ od struktury lub klasy. Struktury i klasy zawieraj¹ im-plementacjê; gdyby interfejs móg³ po nich dziedziczyæ, dziedziczy³by implementa-cjê.

Implementowanie interfejsuAby zaimplementowaæ interfejs, deklarujesz klasê (lub strukturê), która dziedziczypo interfejsie i implementuje wszystkie jego metody. Na przyk³ad:

class Token : IToken{

...public string Name(){

...}

}

Kiedy implementujesz interfejs, musisz upewniæ siê, ¿e ka¿da metoda dok³adnie od-powiada swojemu prototypowi w interfejsie:

n Metoda musi byæ jawnie zadeklarowana jako publiczna, poniewa¿ metoda interfej-su jest niejawnie publiczna.

n Typy zwrotne musz¹ do siebie dok³adnie pasowaæ.

n Nazwy metod musz¹ do siebie dok³adnie pasowaæ.

196 Czêœæ II Zrozumieæ jêzyk C#

n Parametry (jeœli s¹) musz¹ do siebie dok³adnie pasowaæ (³¹cznie z modyfikatoramiref i out, choæ nie params).

Jeœli jest jakaœ ró¿nica, kompilacja nie powiedzie siê, poniewa¿ metoda interfejsu niebêdzie zaimplementowana. Domyœlnie metoda implementuj¹ca metodê interfejsu niejest wirtualna (by³a ju¿ o tym mowa). Jeœli chcesz przes³oniæ implementacjê metodyw kolejnej klasie pochodnej, musisz zadeklarowaæ metodê jako wirtualn¹ (o tym rów-nie¿ wspomnieliœmy). Na przyk³ad:

class Token : IToken{

...public virtual string Name(){

...}

}class IdentifierToken : Token{

...public override string Name(){

...}

}

Zgadza siê to z zasad¹, ¿e s³owo kluczowe virtual deklaruje pierwsz¹ implementacjêmetody (w kolejnych klasach pochodnych mog¹ byæ inne), a metoda niewirtualna jestjedyn¹ implementacj¹.

Klasa mo¿e rozszerzaæ inn¹ klasê oraz implementowaæ interfejs. W takim przypadkuC# nie odró¿nia klasy bazowej od interfejsu bazowego za pomoc¹ s³ów kluczowych,jak robi to np. Java. C# u¿ywa notacji pozycyjnej. Najpierw pisze siê nazwê klasy ba-zowej, potem przecinek, wreszcie nazwê interfejsu bazowego. Na przyk³ad:

interface IToken{

...}class DefaultTokenImpl{

...}class IdentifierToken : DefaultTokenImpl, IToken{

...}

Klasy abstrakcyjneNiemal zawsze okazuje siê, ¿e dany interfejs jest implementowany przez wiele klaslub struktur. Na przyk³ad interfejs IToken móg³by byæ implementowany przez piêæklas, po jednej na ka¿dy typ leksemu w pliku Ÿród³owym C#: IdentifierToken, Keyword-Token, LiteralToken, OperatorToken i PunctuatorToken (móg³byœ równie¿ zdefiniowaæklasy komentarza i odstêpu). W takiej sytuacji czêœci klas pochodnych maj¹ zwykle

Rozdzia³ 12 Dziedziczenie 197

wspóln¹ implementacjê. Na przyk³ad w poni¿szych dwóch klasach powielenie kodujest oczywiste:

class IdentifierToken : IToken{

public IdentifierToken(string name){

this.name = name;}public virtual string Name(){

return name;}...private string name;

}class StringLiteralToken : IToken{

public StringLiteralToken(string name){

this.name = name;}public virtual string Name(){

return name;}...private string name;

}

Powielenie kodu to znak ostrzegawczy; powinieneœ przepisaæ kod tak, aby unikn¹æduplikacji. Mo¿na to jednak zrobiæ dobrze albo Ÿle. Z³y sposób polega na upchniêciucech wspólnych w interfejsie. Jest to niepo¿¹dane, bo wówczas musia³byœ zmieniæ in-terfejs w klasê (poniewa¿ interfejsy nie mog¹ zawieraæ implementacji). Interfejsy wy-myœlono nie bez powodu; zostaw interfejs w spokoju. Aby unikn¹æ duplikacji, nale¿yutworzyæ now¹ klasê, która bêdzie zawieraæ wspóln¹ implementacjê. Na przyk³ad:

class DefaultTokenImpl : IToken{

public DefaultTokenImpl(string name){

this.name = name;}public string Name(){

return name;}...private string name;

}class IdentifierToken : DefaultTokenImpl, IToken{

public IdentifierToken(string name): base(name)

{}...

}

198 Czêœæ II Zrozumieæ jêzyk C#

class StringLiteralToken : DefaultTokenImpl, IToken{

public StringLiteralToken(string name): base(name)

{}...

}

Jest to rozwi¹zanie dobre, ale jeszcze nie doskona³e: mo¿esz teraz tworzyæ instancjeklasy DefaultTokenImpl, co raczej nie ma sensu. Klasa DefaultTokenImpl ma tylko za-pewniaæ wspóln¹ implementacjê; istnieje tylko po to, aby po niej dziedziczyæ (albo nie– ka¿da klasa pochodna sama o tym decyduje). Klasa DefaultTokenImpl jest abstrakcyj-na, w takim samym sensie jak interfejs. Tworzenie instancji interfejsu jest zawsze nie-mo¿liwe, ale chc¹c zadeklarowaæ, ¿e nie wolno tworzyæ instancji klasy, musisz o tymjawnie poinformowaæ za pomoc¹ s³owa kluczowego abstract. Na przyk³ad:

abstract class DefaultTokenImpl{

public DefaultTokenImpl(string name){

this.name = name;}public string Name(){

return name;}private string name;

}

Zwróæ uwagê, ¿e nowa klasa DefaultTokenImpl nie implementuje interfejsu IToken.Mog³aby, ale s³u¿y nie do tego. Klasa abstrakcyjna okreœla wspóln¹ implementacjê,natomiast interfejs okreœla sposób u¿ycia. Zwykle lepiej oddzieliæ te dwa aspekty i po-zwoliæ klasom nieabstrakcyjnym (takim jak StringLiteralToken) samodzielnie decydo-waæ o sposobie implementowania interfejsów:

n Mog¹ dziedziczyæ po DefaultTokenImpl oraz IToken; w takim przypadku DefaultTo-kenImpl.Name staje siê implementacj¹ metody IToken.Name. Oznacza to, ¿e metodaDefaultTokenImpl.Name musi byæ publiczna. Mo¿esz zadeklarowaæ konstruktor De-faultTokenImpl jako chroniony, ale metoda Name musi pozostaæ publiczna, jeœli mabyæ implementacj¹ IToken.Name w klasie pochodnej.

n Mog¹ nie dziedziczyæ po DefaultTokenImpl; w takim przypadku bêd¹ musia³y sameimplementowaæ metodê IToken.Name. Rezygnacja z dziedziczenia mo¿e mieæ ró¿neprzyczyny (choæby prosty fakt, ¿e klasa mo¿e mieæ co najwy¿ej jedn¹ klasê ba-zow¹).

Metody abstrakcyjneTylko klasy abstrakcyjne mog¹ deklarowaæ metody abstrakcyjne. Oznacza to, ¿ew klasie abstrakcyjnej wolno oznaczyæ metodê s³owem kluczowym abstract i zamiasttreœci metody napisaæ œrednik (jak w interfejsie). Metody abstrakcyjne s¹ szczególnieprzydatne wtedy, gdy klasa abstrakcyjna implementuje interfejs, poniewa¿ klasa abs-trakcyjna – tak jak zwyk³e klasy – musi „implementowaæ” wszystkie metody interfej-

Rozdzia³ 12 Dziedziczenie 199

su, po którym dziedziczy. Na przyk³ad poni¿sza klasa DefaultTokenImpl kompiluje siêtylko dlatego, ¿e „implementuje” obie metody dziedziczone po swoim interfejsie:

interface IToken{

int Line();string Name();

}abstract class DefaultTokenImpl : IToken{

public abstract int Line();public string Name(){

return name;}public DefaultTokenImpl(string name){

this.name = name;}private string name;

}

Metoda abstrakcyjna jest niejawnie wirtualna, wiêc w klasach pochodnych musi byæimplementowana przez metody przes³aniaj¹ce. Na przyk³ad:

class LiteralToken : DefaultTokenImpl{

public override int Line(){

...}

}

Klasy zamkniêteU¿ywanie dziedziczenia nie jest ³atwe. Nie ma siê czemu dziwiæ; jeœli tworzysz inter-fejs albo klasê abstrakcyjn¹, œwiadomie piszesz coœ, z czego w przysz³oœci bêdzieszwywodzi³ nowe klasy. Problem w tym, ¿e trudno przewidzieæ przysz³oœæ. ¯eby utwo-rzyæ elastyczn¹ i ³atw¹ w u¿yciu hierarchiê interfejsów, klas abstrakcyjnych orazzwyk³ych klas, potrzeba umiejêtnoœci, wysi³ku i znajomoœci problemu, który próbu-jesz rozwi¹zaæ. Innymi s³owy, jeœli nie tworzysz klasy z zamiarem u¿ycia jej jako kla-sy bazowej, jest ma³o prawdopodobne, ¿e bêdzie dobrze pe³ni³a tak¹ funkcjê. Naszczêœcie C# pozwala u¿yæ s³owa kluczowego sealed, aby zapobiec u¿ywaniu klasyjako klasy bazowej. Na przyk³ad:

sealed class LiteralToken : DefaultTokenImpl, IToken{

...}

Jeœli inna klasa zadeklaruje LiteralToken jako klasê bazow¹, wyst¹pi b³¹d kompilacji.Klasa zamkniêta nie mo¿e deklarowaæ metod wirtualnych. S³owo kluczowe virtual de-klaruje, ¿e jest to pierwsza implementacja metody, która bêdzie przes³aniana w klasachpochodnych, a z klasy zamkniêtej nie mo¿na wywodziæ nowych klas. Zauwa¿ te¿, ¿estruktura jest niejawnie zamkniêta; nie da siê wywodziæ z niej nowych struktur.

200 Czêœæ II Zrozumieæ jêzyk C#

Metody zamkniêteZa pomoc¹ s³owa kluczowego sealed mo¿na równie¿ zadeklarowaæ metodê jako zam-kniêt¹. Oznacza to, ¿e klasa pochodna nie mo¿e przes³aniaæ metody. Zamkn¹æ mo¿natylko metodê przes³aniaj¹c¹; zwi¹zek miêdzy interfejsem oraz s³owami kluczowymivirtual, override i sealed jest nastêpuj¹cy:

n Interfejs wprowadza nazwê metody.

n Metoda wirtualna (virtual) to pierwsza implementacja metody.

n Metoda przes³aniaj¹ca (override) to kolejna implementacja metody.

n Metoda zamkniêta (sealed) to ostatnia implementacja metody.

Rozszerzanie hierarchii dziedziczeniaW poni¿szym æwiczeniu zapoznasz siê z niewielk¹ hierarchi¹ interfejsów i klas, którewspólnie tworz¹ prosty szkielet.

Wskazówka Szkielet ró¿ni siê od biblioteki tym, ¿e biblioteki mo¿na u¿ywaæ tylkobezpoœrednio, w œciœle okreœlony sposób, natomiast ze szkieletu mo¿na korzystaæ po-œrednio, na wiele ró¿nych sposobów, tworz¹c klasy dziedzicz¹ce po rozwa¿nie za-projektowanych interfejsach szkieletu.

Szkielet jest aplikacj¹ Microsoft Windows, która symuluje czytanie pliku Ÿród³owegoC# i klasyfikowanie jego treœci na leksemy (np. identyfikatory, s³owa kluczowe, ope-ratory itd.). W drugim æwiczeniu utworzysz klasê, która dziedziczy po interfejsachszkieletu oraz wyœwietla leksemy pliku Ÿród³owego w polu wzbogaconego tekstu,u¿ywaj¹c kolorowej sk³adni.

Hierarchia dziedziczenia1. Uruchom Microsoft Visual Studio .NET.

2. Otwórz projekt CSharp w folderze \Visual C# Step by Step\Chapter 12\CSharp.

Otworzy siê projekt CSharp.

3. Otwórz plik Ÿród³owy SourceFile.cs w okienku Code.

Klasa SourceFile zawiera prywatne pole tablicowe o nazwie tokens:

private IVisitableToken[] tokens ={

new KeywordToken("using"),new WhitespaceToken(" "),new IdentifierToken("System"),new PunctuatorToken(";"),...

};

Ta tablica zawiera sekwencjê obiektów, które implementuj¹ interfejs IVisitableTo-ken. Symuluj¹ one leksemy prostego pliku Ÿród³owego z programem „helloworld”. Pe³na wersja tego projektu analizowa³aby plik Ÿród³owy o podanej na-

Rozdzia³ 12 Dziedziczenie 201

zwie i tworzy³a leksemy dynamicznie. Klasa SourceFile zawiera te¿ publiczn¹ me-todê o nazwie Accept, która przyjmuje jeden parametr typu ITokenVisitor. Treœæmetody Accept iteruje przez leksemy, wywo³uj¹c ich metody Accept:

public void Accept(ITokenVisitor visitor){

foreach(IVisitableToken token in tokens){

token.Accept(visitor);}

}

Dziêki temu parametr visitor odwiedza kolejno ka¿dy leksem.

4. Otwórz plik Ÿród³owy IVisitableToken.cs w okienku Code.

Interfejs IVistableToken dziedziczy po dwóch innych interfejsach, IVisitable orazIToken:

interface IVisitableToken: IVistable, IToken{}

5. Otwórz plik Ÿród³owy IVisitable.cs w okienku Code.

Interfejs IVisitable deklaruje metodê Accept:

interface IVistable{

void Accept(ITokenVisitor visitor)}

Ka¿dy obiekt w tablicy tokens w klasie SourceFile jest dostêpny za poœrednictweminterfejsu IVisitableToken. Interfejs IVisitableToken dziedziczy metodê Accept. Ozna-cza to, ¿e ka¿dy leksem musi implementowaæ metodê Accept.

6. Otwórz plik Ÿród³owy Source.cs w okienku Code i znajdŸ klasê IdentifierToken.

Klasa IdentifierToken dziedziczy po abstrakcyjnej klasie DefaultTokenImpl oraz pointerfejsie IVisitableToken. Implementuje metodê Accept w nastêpuj¹cy sposób:

void IVisitable.Accept(ITokenVisitor visitor){

visitor.VisitIdentifier(ToString());}

Inne klasy leksemów wygl¹daj¹ podobnie.

7. Otwórz plik Ÿród³owy ITokenVisitor.cs w okienku Code.

Interfejs ITokenVisitor zawiera po jednej metodzie na ka¿dy typ leksemu.

Ca³a ta hierarchia interfejsów, klas abstrakcyjnych i zwyk³ych klas dzia³a tak, ¿emo¿esz napisaæ klasê, która implementuje interfejs ITokenVisitor, utworzyæ instan-cjê tej klasy i przekazaæ j¹ jako parametr metody Accept klasy SourceFile. Naprzyk³ad:

class MyVisitor : ITokenVisitor{

public void VisitIdentifier(string token){

...

202 Czêœæ II Zrozumieæ jêzyk C#

}public void VisitKeyword(string token){

...}...static void Main(){

SourceFile source = new SourceFile();MyVisitor visitor = new MyVisitor();source.Accept(visitor);

}}

W rezultacie ka¿dy leksem pliku Ÿród³owego wywo³a odpowiedni¹ metodê obiektuvisitor. Mo¿esz utworzyæ ró¿ne klasy wizytatorów, które bêd¹ wykonywaæ ró¿nezadania podczas odwiedzania poszczególnych leksemów. Na przyk³ad:

n Wizytator wyœwietlaj¹cy, który wyœwietla plik Ÿród³owy w polu wzbogaconegotekstu.

n Wizytator drukuj¹cy, który przekszta³ca znaki tabulacji w spacje i wyrównuje na-wiasy klamrowe.

n Wizytator pisowni, który sprawdza pisowniê ka¿dego identyfikatora.

n Wizytator doradczy, który sprawdza, czy identyfikatory publiczne zaczynaj¹ siêdu¿¹ liter¹ i czy interfejsy zaczynaj¹ siê du¿¹ liter¹ I.

n Wizytator z³o¿onoœci, który monitoruje g³êbokoœæ zagnie¿d¿enia nawiasów klam-rowych w kodzie.

n Wizytator zliczaj¹cy, który sumuje liczbê wierszy w ka¿dej metodzie, liczbê sk³a-dowych ka¿dej klasy oraz liczbê wierszy w ka¿dym pliku Ÿród³owym.

W poni¿szym æwiczeniu utworzymy klasê ColorSyntaxVisitor, która wyœwietla plikŸród³owy w polu wzbogaconego tekstu, u¿ywaj¹c kolorowej sk³adni (np. s³owa klu-czowe s¹ wyœwietlane na niebiesko).

Pisanie klasy ColorSyntaxVisitor1. W Solution Explorerze kliknij dwukrotnie pozycjê Form1.cs, aby wyœwietliæ formê

Color Syntax w oknie Designer View. Forma zawiera przycisk Open, który otwieraplik przeznaczony do podzia³u na leksemy, oraz pole wzbogaconego tekstu, którewyœwietla leksemy.

Rozdzia³ 12 Dziedziczenie 203

2. Otwórz plik Ÿród³owy Form1.cs w okienku Code i znajdŸ dwa prywatne pola o na-zwach codeText oraz Open:

private System.Windows.Forms.RichTextBox codeText;private System.Windows.Forms.Button Open;

W³aœnie te dwa pola implementuj¹ pole wzbogaconego tekstu oraz przycisk, któreprzed chwil¹ widzia³eœ na formie.

3. W okienku Code znajdŸ metodê Form1.Open_Click.

Ta metoda jest wywo³ywana po klikniêciu przycisku Open. Musisz j¹ napisaæ tak,¿eby wyœwietla³a leksemy w polu wzbogaconego tekstu. Zmieñ metodê Form1.Open_Click w nastêpuj¹cy sposób:

private void Open_Click(object sender, System.EventArgs e){

SourceFile source = new SourceFile();ColorSyntaxVisitor visitor =new ColorSyntaxVisitor(codeText);source.Accept(visitor);

}

4. Otwórz plik Ÿród³owy ColorSyntaxVisitor.cs w okienku Code.

Klasa ColorSyntaxVisitor jest czêœciowo zaimplementowana. Dziedziczy po inter-fejsie ITokenVisitor i zawiera ju¿ dwa pola oraz konstruktor, który inicjuje referen-cjê do docelowego pola wzbogaconego tekstu. Twoim zadaniem bêdziezaimplementowaæ metody dziedziczone po interfejsie ITokenVisitor oraz wyœwiet-liæ leksemy w docelowym polu wzbogaconego tekstu.

5. W okienku Code dopisz metodê Write do klasy ColorSyntaxVisitor:

private void Write(string token, Color color){

target.AppendText(token);target.Select(index, index + token.Length);index += token.Length;target.SelectionColor = color;

}

Ta metoda do³¹cza leksem do pola wzbogaconego tekstu, wyœwietlaj¹c go w okre-œlonym kolorze. Bêd¹ j¹ wywo³ywaæ pozosta³e metody.

204 Czêœæ II Zrozumieæ jêzyk C#

6. W okienku Code napisz pozosta³e metody klasy ColorSyntaxVisitor.

U¿yj koloru Color.Blue dla s³ów kluczowych, ColorGreen – dla litera³ów ³añcucho-wych, a Color.Black – dla pozosta³ych leksemów.

void ITokenVisitor.VisitComment(string token){

Write(token, Color.Black);}

void ITokenVisitor.VisitIdentifier(string token){

Write(token, Color.Black);}

void ITokenVisitor.VisitKeyword(string token){

Write(token, Color.Blue);}

void ITokenVisitor.VisitOperator(string token){

Write(token, Color.Black);}

void ITokenVisitor.VisitPunctuator(string token){

Write(token, Color.Black);}

void ITokenVisitor.VisitStringLiteral(string token){

Write(token, Color.Green);}

void ITokenVisitor.VisitWhiteSpace(string token){

Write(token, Color.Black);}

7. Z menu Build wybierz polecenie Build Solution.

W razie potrzeby popraw b³êdy i ponownie zbuduj program.

8. Z menu Debug wybierz polecenie Start Without Debugging.

Pojawi siê forma Color Syntax.

9. Kliknij przycisk Open na formie.

W polu wzbogaconego tekstu uka¿e siê kod ze s³owami kluczowymi wyœwietlo-nymi na niebiesko, a litera³ami ³añcuchowymi – na zielono.

Rozdzia³ 12 Dziedziczenie 205

10. Zamknij formê.

Wrócisz do Visual Studia .NET.

Praca z wieloma interfejsamiKlasa mo¿e mieæ najwy¿ej jedn¹ klasê bazow¹, ale dowolnie du¿o interfejsów. Klasamusi implementowaæ wszystkie metody odziedziczone po wszystkich interfejsach.Klasy abstrakcyjne i zamkniête musz¹ przestrzegaæ tych samych regu³ dziedziczeniaco zwyk³e klasy. Interfejs nie mo¿e dziedziczyæ po ¿adnej klasie (to wprowadzi³obydo niego implementacjê), ale mo¿e dziedziczyæ po dowolnie wielu interfejsach (bo niewprowadza to ¿adnej implementacji).

Sk³adniaJeœli interfejs, struktura lub klasa dziedziczy po wiêcej ni¿ jednym interfejsie, interfej-sy umieszcza siê na podzielonej przecinkami liœcie. Jeœli klasa ma równie¿ klasê ba-zow¹, interfejsy nale¿y wymieniæ po klasie bazowej. Na przyk³ad:

class IdentifierToken : DefaultTokenImpl, IToken, IVisitable{

...}

Jawna implementacja interfejsuIstnieje drugi sposób implementowania metody interfejsu przez klasê lub strukturê.W tym przypadku nazwê metody jawnie kwalifikuje siê nazw¹ interfejsu i nie u¿ywamodyfikatora dostêpnoœci. Jeœli np. interfejs IVisitable wygl¹da tak:

interface IVisitable{

void Accept(IVisitor visitor);}

mo¿na zaimplementowaæ go w taki sposób:

class IdentifierToken : DefaultTokenImpl, IToken, IVisitable{

...void IVisitable.Accept(IVisitor visitor){

...}

}

206 Czêœæ II Zrozumieæ jêzyk C#

Nazywamy to jawn¹ implementacj¹ interfejsu (ang. Explicit Interface Implementation,EII). Metoda EII jest w rezultacie prywatna w implementuj¹cej j¹ strukturze lub kla-sie. Na przyk³ad:

IdentifierToken token = new IdentifierToken("token");token.Accept(visitor); // b³¹d kompilacji: metoda niedostêpna

Mo¿na jednak wywo³aæ metodê przez jawnie nazwany interfejs, u¿ywaj¹c rzutowa-nia. Na przyk³ad:

IdentifierToken token = new IdentifierToken("token");((IVisitable)token).Accept(visitor); // w porz¹dku

Zatem metoda EII nie jest ani ca³kowicie prywatna, ani ca³kowicie publiczna, co wyja-œnia brak kwalifikatora dostêpnoœci. Metody EII s¹ przydatne z co najmniej dwóchpowodów.

Po pierwsze, tworz¹ jeszcze œciœlejszy podzia³ miêdzy interfejsem, który okreœla, jaknale¿y u¿ywaæ obiektu (typem u¿ycia), a klas¹, która okreœla, jak obiekt jest tworzonyi implementowany (klas¹ kreacji). Metody EII s¹ prywatnymi metodami klasy, wiêc¿eby je wywo³aæ, trzeba spojrzeæ na obiekt przez jego interfejs.

Po drugie, metody EII rozwi¹zuj¹ potencjalny problem konfliktu nazw miêdzy wielo-ma interfejsami. Przypuœæmy, ¿e interfejsy IToken oraz IVisitable zawieraj¹ metodêo nazwie ToString:

interface IToken{

...string ToString();

}interface IVisitable{

...string ToString();

}

Czy ci siê to podoba, czy nie, poni¿sza klasa deklaruje jedn¹ metodê ToString, którajest implementacj¹ obu powy¿szych metod:

class IdentifierToken: IToken, IVisitable{

...public string ToString(){

...}

}

EII pozwala utworzyæ dwie ró¿ne implementacje metody ToString, po jednej na ka¿d¹metodê interfejsu. Na przyk³ad:

class IdentifierToken: IToken, IVisitable{

...string IToken.ToString(){

...}

Rozdzia³ 12 Dziedziczenie 207

string IVisitable.ToString(){

...}

}

Zwróæ uwagê, ¿e metoda EII nie jest wirtualna (i nie mo¿e byæ). Nie da siê jej równie¿przes³oniæ w klasie pochodnej.

Podsumowanie kombinacji s³ów kluczowychW poni¿szej tabeli wymieniono poprawne (tak) i niepoprawne (nie) kombinacje s³ówkluczowych.

interface abstract class class sealed class struct

abstract nie tak nie nie nie

new tak(1) tak tak tak nie(2)

override nie tak tak tak nie(3)

private nie tak tak tak tak

protected nie tak tak tak nie(4)

public nie tak tak tak tak

sealed nie tak tak tak nie

virtual nie tak tak nie nie

(1) Interfejs mo¿e rozszerzaæ inny interfejs i wprowadzaæ now¹ metodê o takiej samejsygnaturze.

(2) Struktura niejawnie dziedziczy po klasie System.Object zawieraj¹cej metody, któ-re struktura mo¿e ukryæ.

(3) Struktura niejawnie dziedziczy po klasie System.Object zawieraj¹cej metody wir-tualne.

(4) Struktura jest niejawnie zamkniêta i nie mo¿na po niej dziedziczyæ.

Jeœli chcesz przejœæ do nastêpnego rozdzia³ul Nie wy³¹czaj Visual Studia .NET i zacznij czytaæ rozdzia³ 13.

Jeœli chcesz opuœciæ Visual Studio .NETl Z menu File wybierz polecenie Exit.

Jeœli zobaczysz okno dialogowe Save (Zapisz), kliknij przycisk Yes (Tak).

208 Czêœæ II Zrozumieæ jêzyk C#

Rozdzia³ 12 Dziedziczenie 209

Krótki przewodnik

Aby Wykonaj nastêpuj¹ce czynnoœci

Utworzyæ klasê pochodn¹klasy bazowej

Napisz nazwê nowej klasy, dwukropek i nazwêklasy bazowej. Na przyk³ad:

class Derived : Base{

...}

Wywo³aæ konstruktorklasy bazowej

Podaj listê parametrów konstruktora klasybazowej przed treœci¹ konstruktora klasypochodnej. Na przyk³ad:

class Derived : Base{

...public Derived(int x) : Base(x){

...}...

}

Zadeklarowaæ metodêwirtualn¹

Podczas deklarowania metody u¿yj s³owakluczowego virtual. Na przyk³ad:

class Animal{

public virtual string Talk(){

...}

}

Zadeklarowaæ interfejs U¿yj s³owa kluczowego interface. Na przyk³ad:

interface IDemo{

string Name();string Description();

}

Zaimplementowaæinterfejs

Zadeklaruj klasê, u¿ywaj¹c tej samej sk³adni cow przypadku dziedziczenia, a nastêpniezaimplementuj wszystkie funkcje sk³adoweinterfejsu. Na przyk³ad:

class Test : IDemo{

public string IDemo.Name(){

...}public string IDemo.Description(){

...}

}