Programovacφ jazyk C# je natolik podobn² jazyku C++, ₧e to programßtora zvyklΘho na C++ snadno svede k nepozornosti. V²sledkem pak m∙₧e b²t nejedno nemilΘ p°ekvapenφ, nebo¥ n∞kterß pravidla jsou v jazyce C# v²razn∞ odliÜnß. Zde si povÜimneme n∞kolika ·skalφ hrozφcφch kv∙li t∞mto rozdφl∙m p°i prßci s metodami.
P°etφ₧enΘ metody
Podobn∞ jako C++, i C# dovoluje p°et∞₧ovat metody. Postup p°i urΦenφ, kterou metodu mß p°ekladaΦ zavolat, je v ka₧dΘm z jazyk∙ pon∞kud jin². To nejspφÜ nikoho nep°ekvapφ, nebo¥ se liÜφ i systΘm jejich datov²ch typ∙. NicmΘn∞ zßkladnφ myÜlenka je v obou jazycφch stejnß: Sestavφ se mno₧ina "kandidßt∙", tedy metod se stejn²m identifikßtorem a se stejn²m poΦtem formßlnφch parametr∙, a mezi nimi se vybere metoda, jejφ₧ signatura (v C++ bychom °ekli prototyp) nejlΘpe odpovφdß typ∙m skuteΦn²ch parametr∙. (O tom budeme hovo°it jako o pravidlu "lepÜφ shody".) Jist∞ vφte, ₧e v C++ nenφ mo₧nΘ rozliÜovat p°etφ₧enΘ metody na zßklad∞ toho, zda se jejich parametry p°edßvajφ hodnotou nebo odkazem. V C# vÜak rozliÜovat p°etφ₧enΘ metody podle zp∙sobu p°edßvßnφ parametr∙ lze, nebo¥ ten je souΦßstφ signatury. To znamenß, ₧e v jednΘ t°φd∞ m∙₧eme mφt vedle sebe nap°. metody
void f(int i); // Parametr p°edßvan² hodnotou
void f(ref int i); // Parametr p°edßvan² odkazem
void f(out int i); // V²stupnφ parametr
Vysv∞tlenφ, proΦ je to v C# mo₧nΘ, je jednoduchΘ: Modifikßtory ref, resp. out, je₧ oznaΦujφ parametry p°edßvanΘ odkazem, resp. v²stupnφ parametry, zapisujeme takΘ p°ed skuteΦnΘ parametry p°i volßnφ. Chceme-li nap°. zavolat metodu f() a jako v²stupnφ parametr jφ p°edat prom∞nnou x, napφÜeme f(out x);.
V C#, stejn∞ jako v C++, lze definovat takΘ metody s prom∞nn²m poΦtem parametr∙. V C++ k tomu slou₧φ v²pustka (...), v C# modifikßtor params. Tento modifikßtor ovÜem v C# k rozliÜenφ p°etφ₧en²ch metod pou₧φt nelze.
Zastφn∞nφ metody
V C#, stejn∞ jako v C++, p°edstavuje t°φda obor viditelnosti identifikßtor∙. Jestli₧e v C++ deklarujeme v odvozenΘ t°φd∞ stejn² identifikßtor jako v p°edkovi, zastφnφme tφm identifikßtor z p°edka; v C# platφ podobnΘ pravidlo pro v∞tÜinu identifikßtor∙, v p°φpad∞ metod je vÜak situace slo₧it∞jÜφ. Ukß₧eme si p°φklad, kdy se v d∙sledku toho program v C# chovß jinak ne₧ analogickß konstrukce v C++.
Nejprve deklarujeme t°φdu A, je₧ bude obsahovat ve°ejn∞ p°φstupnou metodu f() typu void bez parametr∙:
class A // C# { public void f(){/* ... */} }
(Podobn∞ jako v C++ i v C# platφ, ₧e pokud v n∞jakΘ t°φd∞ nedeklarujeme ₧ßdn² konstruktor, doplnφ do nφ p°ekladaΦ ve°ejn∞ p°φstupn² konstruktor bez parametr∙; proto si m∙₧eme dovolit deklaraci konstruktoru ve t°φd∞ A vynechat.)
Dßle deklarujeme t°φdu B jako potomka t°φdy A. Ve t°φd∞ B deklarujeme op∞t metodu f(), tentokrßt ovÜem s jednφm parametrem typu int:
class B: A // C#
{ new public void f(int i){} static void Main(string[] args)
{
B b = new B(); b.f(); } }
V metod∞ Main() vytvo°φme instanci t°φdy B a zavolßme metodu f() bez parametr∙; v C# to je v po°ßdku, nebo¥ metoda f(int i), deklarovanß ve t°φd∞ B, nenφ zastφn∞na metodou f() deklarovanou v p°edkovi. Na druhΘ stran∞ analogick² program v C++,
class A // C++ {public: void f(){} }; class B: public A {public: void f(int i){} }; void main() {
C *b = new B; b->f(); }
se nepoda°φ p°elo₧it, nebo¥ p°ekladaΦ ohlßsφ n∞co jako "p°φliÜ mßlo parametr∙ p°i volßnφ metody f()". V C# platφ, ₧e deklarujeme-li v odvozenΘ t°φd∞ metodu s identifikßtorem f, zastφnφme tφm metodu p°edka se stejnou signaturou. Metody se stejn²m identifikßtorem, ale s rozdφlnou signaturou, z∙stanou viditelnΘ.
Proto tφm, ₧e jsme ve t°φd∞ B deklarovali metodu f(int i), jsme nezastφnili metodu f()zd∞d∞nou od t°φdy A, nebo¥ se liÜφ jejich signatury.
Zd∞d∞nΘ a vlastnφ metody
Z p°edchozφho p°φkladu by se mohlo zdßt, ₧e zd∞d∞nΘ metody majφ v C# stejnΘ postavenφ jako metody definovanΘ v odvozenΘ t°φd∞. To vÜak nenφ pravda; podφvejme se na nßsledujφcφ p°φklad:
using System; // C# class A { public void f(int i)
{
Console.WriteLine("A.f(int)");
} }; class B: A { public void f(long i)
{
Console.WriteLine("B.f(long)");
} }class Program { static void Main(string[] args)
{
B b = new B(); b.f('a');
} }
V p°edkovi, ve t°φd∞ A, jsme definovali metodu f(int i), zatφmco v potomkovi metodu f(long i). V metod∞ Main() vytvo°φme instanci t°φdy B a zavolßme metodu f() se skuteΦn²m parametrem typu char. (To lze, nebo¥ v C# je stejn∞ jako v C++ k dispozici implicitnφ konverze typu char na celoΦφselnΘ typy.)
Podle pravidla o "lepÜφ shod∞" bychom mohli oΦekßvat, ₧e se zavolß zd∞d∞nß metoda f(int i). V²stup tohoto programu nßs vÜak p°esv∞dΦφ, ₧e se volß metoda f(long i) definovanß v odvozenΘ t°φd∞ B. Stejnß metoda se zavolß i v p°φpad∞, ₧e metodu f() zavolßme p°φkazem b.f(1); i kdy₧ se v tomto p°φpad∞ typ skuteΦnΘho parametru p°esn∞ shoduje s typem formßlnφho parametru zd∞d∞nΘ metody.
Na druhΘ stran∞, zm∞nφme-li deklarace metod f() takto:
class A // C# { public void f(long i)
{Console.WriteLine("A.f(long)");} }; class B: A { public void f(short i)
{Console.WriteLine("B.f(short)");} }
a zavolßme-li metodu f() p°φkazy
int a = 1; b.f(a);
zavolß se zd∞d∞nß metoda, nebo¥ konverze typu int na short nem∙₧e v C# prob∞hnout implicitn∞.
Modifikßtor new
U₧ jsme si °ekli, ₧e definujeme-li v odvozenΘ t°φd∞ slo₧ku se stejn²m identifikßtorem jako v p°edkovi, zd∞d∞nou slo₧ku zastφnφme. To ve skuteΦnosti nepot°ebujeme p°φliÜ Φasto. Proto zavßdφ jazyk C# modifikßtor new, kter²m p°ekladaΦi vysv∞tlujeme, ₧e nejde o p°eklep a ₧e si zastφn∞nφ zd∞d∞nΘ slo₧ky opravdu p°ejeme.
Vynechßme-li tento modifikßtor, ohlßsφ p°ekladaΦ varovßnφ; podobn∞ to dopadne, uvedeme-li tento modifikßtor v deklaraci, kterß nic nezasti≥uje.
Modifikßtor new nesmφme v C# pou₧φt spolu s modifikßtorem override. M∙₧eme ho vÜak pou₧φt spolu s modifikßtorem virtual, tj. v odvozenΘ t°φd∞ smφme deklarovat nap°. metodu public new virtual void f() {/* ... */} Takovßto deklarace p°eruÜφ hierarchii virtußlnφch metod a znamenß novou implementaci, nezßvislou na implementaci z p°edka. (NejspφÜ nep∙jde o nijak Φasto pou₧φvanou konstrukci, ale n∞jak² d∙vod, proΦ to tv∙rci jazyka zavedli, asi bude.)
P°φstupovß prßva
V C++ se p°i vyhledßvßnφ p°etφ₧en²ch metod neuplat≥ujφ p°φstupovß prßva: Nejprve se zjistφ, kterß metoda nejlΘpe odpovφdß typ∙m skuteΦn²ch parametr∙ p°i volßnφ, a pak se urΦφ, zda ji lze zavolat (zda to dovolujφ p°φstupovß prßva). V C# se p°φstupovß prßva uplat≥ujφ u₧ p°i vyhledßvßnφ "kandidßt∙". Podφvejme se na p°φklad: V p°edkovi, ve t°φd∞ A, deklarujeme ve°ejn∞ p°φstupnou metodu f() bez parametr∙. V potomkovi, ve t°φd∞ B, ji zastφnφme chrßn∞nou metodou se stejnou signaturou. Ve t°φd∞
Program, v metod∞ Main(), vytvo°φme instanci t°φdy B a zavolßme pro ni metodu f():
using System; // C# class A { public void f()
{
Console.WriteLine("A.f");
} }class B: A { new protected void f() {
Console.WriteLine("B.f"); } }class Program { static void Main()
{
B b = new B(); b.f();
} }
Tento program zavolß metodu f() zd∞d∞nou po t°φd∞ A. Metoda f(), deklarovanß ve t°φd∞ B, nenφ p°φstupnß, a proto ji p°ekladaΦ p°i v²b∞ru kandidßt∙ nebere v ·vahu. Podφvejme se jeÜt∞ na analogick² program v C++:
#include <iostream.h> // C++ class A { public: void f()
{ cout << "A.f";
} }; class B: public A { protected: void f()
{ cout << "B.f";
} }; void main() {
B *b = new B; b->f(); }
Pokus o p°eklad tohoto programu skonΦφ chybou - p°ekladaΦ oznßmφ, ₧e metoda B::f() nenφ dostupnß, nebo¥ p°φstupovß prßva vzal v ·vahu a₧ po urΦenφ metody, kterou je t°eba volat.
Filozofii jazyka C++, pokud jde o aplikaci p°φstupov²ch prßv, lze shrnout do v∞ty, ₧e "zm∞na p°φstupov²ch prßv nesmφ zm∞nit chovßnφ programu". (Tak to formuloval B. Stroustrup v jednΘ z knih o C++.)
Jazyk C# se °φdφ spφÜe filozofiφ "co nenφ p°φstupnΘ, o tom nevφm, to neberu v ·vahu". Zm∞na p°φstupov²ch prßv v tomto jazyce proto m∙₧e vΘst k pom∞rn∞ zßsadnφ zm∞n∞ v chovßnφ programu: V zßvislosti na pou₧itΘm modifikßtoru se budou volat r∙znΘ metody!
Nenφ jazyk jako jazyk
Tolik prozatφm k nejmarkantn∞jÜφm odliÜnostem C# a C++, pokud jde o zachßzenφ s metodami. Tφm ovÜem nejsou vyΦerpßna vÜechna nebezpeΦφ vypl²vajφcφ ze shodnΘ Φi podobnΘ syntaxe a rozdφlnΘ sΘmantiky programov²ch konstrukcφ v r∙zn²ch jazycφch. K jazyku C#, v n∞m₧ takovΘ zßm∞ny hrozφ pom∞rn∞ Φasto, a k jeho porovnßnφ s jin²mi programovacφmi jazyky se proto jeÜt∞ n∞kdy vrßtφme.