Generisk programmering i C# på .net del 2

Indledning

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.

Nedarvning, casts og generics i .Net

Nedarvning

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.

 

Sådan gør man i praksis.

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.

 

Cast

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.

 

Generics

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.