Denne artikel
skal handle om nedarvning, som er en vigtig del af objekorienteret udvikling,
samt casts, som hænger meget tæst sammen med nedarvning og sidst men ikke
mindst om generics.
Når du i din
kode har nogle klasser, der har noget tilfælles, er det værd at tænke igennem
om de måske skal have en fælles base klasse.
Måske har du
før hørt om 'is a' (her oversat til 'er en' uden hensyntagen til dansk
grammatik) forholdet.
Det går ud på
at man skal kunne sige at en nedarvet klasse 'er en' base klasse.
Eksempelvis kan
man lave en base klasse der hedder 'køretøj'.
Nu kan vi så
tilføje en klasse, der hedder bil og se at det er ok at lade den nedarve fra
køretøj, da en bil 'er en' (et) køretøj. På samme måde med en cykel.
Vælger vi
derimod at tilføje et fly, vil det ikke kunne nedarve fra køretøj, da et fly
ikke er et køretøj (et generelt fly kan også være et vandlfly og har så ikke hjul,
men her kunne man så lave et nedarvningshieraki for fly).
Ønsker man at
tilføje fly til hierakiet kunne man lave en baseklasse for køretøj, der hedder
transportmiddel. Herfra kunne man så nedarve et fly, evt. Kunne man lægge et
lufttransport middel ind imellem.
For nogle kan
det at lave nedarvninger, være omgivet af en slags mystik at lave nedarvninger.
Men i
virkeligheden er det ret simpelt, at lave en base klasse og arve fra denne.
Det er et godt
princip at man ikke bare skal lave nedarvninger af alting, men jeg vil opfordre
dig til at prøve at lave en nedarvning bare for at prøve det.
Jeg kommer med
et eksempel, men jeg synes du skal finde på dit helt eget eksempel, for du får
ikke noget ud af at kopierer min kode - du får kun noget ud af at lave din egen
kode.
Eksempel:
Jeg har lavet
en asp.net form, der dynamisk, kan tilføje felter til en asp.net form.
Der findes
mange forskellige slags felter, men de har alle det tilfælles at de er felter,
så jeg har lavet en base klasse kaldet Field.
Denne base
klasse indeholder nogle forskellige felter, som id, feltets værdi,er feltet
mandatory osv.
Alle disse ting
er fælles for et felt på en asp.net form.
Jeg har så
lavet nogle nedarvninger som eks. Et strenfelt, en multivaluefelt og et
truefalsefelt.
Om alle disse
kan man sige at de 'er et' felt, og dermed et det korrekt at nedarve fra dem.
Det som man får
ud af et sådan nedarvningshieraki er at fælles funktionalitet ikke dubleres,
men lægges i den fælles base klasse, hvor denså lettere kan vedligeholdes.
Desuden kan man
behandle dem polymorfisk, men det skal næste artikel i serien omhandle.
Det er ikke
sikkert at du har tænkt over det før, men man kan kun caste indenfor et
nedarvningshieraki.
Alternativt kan
man dog i .net lave definitioner af hvordan man laver explicitte og implicitte
casts mellem type fra forskellige nedarvningshierakier.
En implicit
cast, er en cast hvor man ikke bruger en cast operatorerne ( as eller
parenteser).
En explicit
cast er så en cast hvor man bruger en af de to cast operatorer.
Caster man til
en base klasse (upcast ikke at forveksle med opkast;-) behøver man ikke at lave
explicit cast, da en bil er et køretøj.
Caster man
derimod den anden vej (downcast), skal man bruge en cast operator, da et
køretøj ikke ubetinget er en cykel. Desuden skal man være opmærksom på
exceptions når man downcaster, da man ikke kan være sikker på at et køretøj er
en bil.
Eksempel:
Car myCar = new
Car;
Vehicle
myVehicle = Car; //implicit cast en bile r jo et køretøj
Car myNewCar =
(Car) myVehicle; //explicit cast vi véd ikke om køretøjet er en bil
Car myVeryNewCar = myVehicle as Car
//explicit cast
Bruger du as
operatoren og det mislykes at caste,vil myNewCar have værdien null.
Bruger du
derimod parenteser til at caste med vil runtime environmentet kaste en
exception, hvis det mislykkes.
Som nævnt kan
man definerer explicitte og implicitte casts i .net, men det skal denne artikel
ikke handle om.
Arbejder på
klasser nedarvet fra en speciel base klasse.
Har jeg eks. En
klasse (en DAL klasse)der kan hente en DataRow med et køretøj udfra et id og i
samme klasse en funktion der kan mappe en DataRow til et køretøj af en bestemt
slags vil jeg i base klasse være nødt til at mappe til et køretøj, da jeg ikke
kender typen af den specifikke implementation af et køretøj i base klassen(eks.
En bil).
Dermed vil jeg
i den specifikke DAL klasse være nødt
til at caste køretøjet fra mappningen til en bil. Og ligeledes i de andre
specifikke klasser.
Her er det at
generics komme ind i billedet og hjælper til.
I min DAL base
klasse kan jeg bare definerer den sådan her:
public abstract class DALBase <T>
where T : Vehicle
Skal vi starte
med at se på hvad de forskellige del betyder:
<T> viser
at vi arbejder med generics og har defineret en type kaldetT, så når vi
nedarber skal vi sige at vi arver fra DALBase med type Car, det gøre sådan her:
Public class Car :
Vehicle<Car>.
Linjen med
where betyder at typen T skal være af typen Vehicle.
Nu kan jeg lave
en Map metode, der mapper til en T (som I følge specifikationen skal være
nedarvet fra Vehicle).
Som det kan ses,
kan generics (overraskende nok) bruges til at lave generel funktionalitet.