Tutorial - OOP C# Generisk klass

1. Introduktion

I denna tutorial så kommer jag utgå ifrån den applikationen som vi byggde tidigare när vi kunde administrera vår lista med bilobjekt. Den applikationen skall nu byggas ut och göras om så att den fungerar med det arv vi skapade i en tidigare tutorial så att vi kan lagra fordon, både bilar och lastbilar, i vår applikation.

2. Skapa projektet

Jag skapar ett nytt projekt som jag döper till VehicleCollection och sedan överför jag följande filer till projektet;

  • Program.cs (från CarDemoCollection)
  • Vehicle.cs (från VehicleDemo)
  • Car.cs (från VehicleDemo)
  • Lorry.cs (från VehicleDemo)

Det går att överföra filer på flera sätt, kopiera filerna till projektet, skapa filerna och bara kopiera koden. Du hittar säkert det sätt som du känner dig mest bekväm med.

När du har fått alla filer på plats så behöver du kolla att alla filer använder samma namespace (namnrymd) annars kommer filerna inte kunna se varandra när programmet körs. Detta är extra viktigt att tänka på när vi återanvänder och kopierar gamla koder.

Jag väljer att högerklicka på projektet och sedan väljer jag Add->Add Files from Folder, välj sedan att kopiera filerna till ditt nya projekt. Flyttar du dem så försvinner de från ditt tidigare projekt och om du väljer att länka dem så får du svårt att hålla ordning på dina projekt samtidigt som du ändrar i den filen som du kanske skulle vilja spara.

Bild på hur jag importerar filer

När allt är på plats så kan du testa att kompilera och köra applikationen för att se att det fungerar.

2.1 Uppgiften

Det vi vill göra i denna tutorial är att bygga ihop våra två tidigare applikationer. Egentligen handlar det om att skapa en ny typ av lista som lagrar både bilar och lastbilar och sedan jobba med den.

Om jag hade gjort detta som en vanlig arbetsuppgift i ett skarpt projekt där det gäller att göra det så fort/billigt som möjligt och om jag anser att jag har koll på vad som skall göras så hade jag direkt gått in på att byta ut vilken typ av objekt som kan lagras i befintlig lista och gjort de ändringar som behövs. Problemet med detta i utbildningssyfte är att då får vi skriva/ändra en hel del kod utan möjlighet att kompilera och testa att det fungerar på vägen. Därför väljer jag inte den modellen utan istället så bygger jag den nya listan parallellt med den gamla och när vi fått den nya att fungera fullt ut så tar vi steg för steg bort de kvarvarande resterna av den gamla. Inte helt tidseffektivt men jag tror att det är rätt modell för att ni skall kunna hänga med på bästa sätt.

3. Arbetet

Dags att börja koda och göra om vår applikation.

3.1 Skapa den nya samlingen

Eftersom vi skall bygga en lista som lagrar alla typer av Vehicle så skall vi skapa en sådan lista. När vi skapade vår carList så deklarerade vi denna i Mainmetoden med resultat att vi hela tiden fick skicka med denna lista som argument till olika metoder och även ta emot den modifierade listan som returvärde ibland om vi hade påverkat den. Vi skall nu testa att göra det på ett annat sätt och deklarerar denna listan i klassen vilket gör att den blir synlig i hela klassen och i alla klassens metoder. Vi måste också gör denna listan till en klassvariabel och det gör vi genom att deklarera den med nyckelordet static för att tala om att den skall tillhöra hela klassen och inte något objekt. Nu skall vi ändå inte skapa ett objekt av denna klassen så rent tekniskt spelar det ingen roll, men vi får ändå inte glömma att deklarera den som static, annars fungerar det inte.

Deklaration av vehicleList

3.2 Metoden addVehicleAtStart()

Nu gäller det att fundera på hur vi skall ta oss an förändringarna i vårt program. Jag testar att börja i metoden Main() och stegar mig fram ett steg i taget och förändrar allt som behöver förändras.

Det första som jag stöter på är metoden addCarAtStart() som lägger in tre bilar i vår lista. Vi skapar en ny metod som vi döper till addVehicleAtStart() och som skapar några objekt av klassen Car och några objekt av klassen Lorry och lägger i vår nya lista.

Anrop till metoden addVehicleAtStart()

Vi märker också att vi inte behöver returnera listan från metoden som vi gjorde tidigare.

Metoden addVehicleAtStart()

Kompilera applikationen och se till att det inte blir några error.

3.3 Metoden menu()

Vi går nedåt i vår applikation och ser att nästa sak som vi behöver ändra är anropet till metoden menu() som sker i vår switch-sats. Här skickas nu listan med bilar in till metoden, det vill vi ju inte att den skall göra längre. Denna metoden kan vi prova att skriva om istället för att skriva ny. När vi får vår nya meny att fungera så kommer vi ju inte behöva den gamla.

Ta bort carList som inparameter, byt lista som skall räknas antal objekt ifrån och ändra lite i menyvalen så att det passar bättre för våra nya behov. Vi gör de ändringar som vi nu vet att vi behöver göra, det kanske är så att vi måste in här och ändra igen senare. Lägg till ett nytt val att lägga till Lastbil, de andra alternativen kan vara som som de är för tillfället.

metoden menu()

När vi ändrar i en metod så ser vi till att ändra det som behövs i kommentarerna för metoden. Det är tyvärr inte så ovanligt att kommentarer inte ändras när koden ändras. Då får nästa utvecklare en spännande utmaning att försöka få ordning på en applikation som är buggig när koden visar en sak och kommentarerna säger något annat.

Glöm nu inte bort att ändra i switch-satsen så att det matchar det som vi skriver i vår nya meny.

Ändrad switch-sats

Kompilera och testa att det nu visas upp 3 Fordon i vår meny. Hos mig fungerar det, bra då är vi på rätt väg.

Dags att ge sig på de olika delarna i vår meny.

3.4 addCar()

Vi väntar lite med det första valet, skriv ut en lista, och börjar med att ändra metoden addCar() så att den fungerar.

Vi har en fungerande metod där det enda som behöver ändras är att lagra bilar till en annan lista. Därför väljer jag att ändra i befintlig metod istället för att skapa en nästan identisk kopia.

Det som skall ändras är följande;

  • Ingen lista behöver skickas in som parameter till metoden.
  • Bilar lagras i rätt lista i metoden.
  • Ingen lista behöver returneras.
  • Kommentarerna behöver ändras till metoden.
  • Anropet från switch-satsen behöver ändras.

Prova gärna att lösa dessa ändringar på egen hand innan du kikar på koderna som du hittar nedan.

Gömda koder [klicka för att visa]

Metoden addCar()

Anropet från switch-satsen

Kompilera och testa att det går att lägga till en bil.

3.5 addLorry()

Dags att lägga till vår första lastbil. Den enda skillnaden är ju att vi till vår lastbil har en medlemsvariabel till och det är lasten (load) som har datatypen int. Det enklaste är ju att kopiera metoden addCar och sedan lägga till det som vi behöver lägga till. Sedan ändrar vi det som eventuellt behöver ändras, namn och lite annat småpill och slutligen så gör vi ett korrekt anrop från vår switch-sats.

Prova gärna att lösa dessa ändringar på egen hand innan du kikar på koderna som du hittar nedan.

Gömda koder [klicka för att visa]

Metoden addLorry()

Anropet från switch-satsen

Kompilera och testa att det går att lägga till en bil.

Int16

I koden så hade vi tidigare valt att konvertera tillverkningsår med Int16 och troligtvis har du skrivit av eller kopierat denna kod för att konvertera lastkapaciteten på samma sätt. Int16 ger utrymme för ett värde som sträcker sig upp till 32767 kg. Jag är ingen expert på lastbilar men jag misstänker att detta räcker. Det innebär dock att om du skriver ett större tal när du testar din applikation så kommer applikationen att krascha och det är inte alltid säkert att det är så tydligt varför.

Hur löser man detta?

  • Ett alternativ är att konvertera med Int32 vilket skapar utrymme för en lastkapacitet för ca 2.1 miljarder kg vilket borde räcka. Gott och väl.
  • Testa med try-catch för att fånga upp en felaktig inmatning.
  • Inte testa med större tal än vad som programmet klarar av.

Du bestämmer själv hur du vill göra, jag ändrar till Int32 så behöver jag inte få ett kraschande program. Att mina bilar kan lasta världsdelar och världshav är bara en bonus.

3.6 removeVehicle()

Kan vi lägga till så vill jag kunna ta bort. Därför bygger vi om metoden removeCar() till removeVehicle(). Vad skall vi göra i metoden?

  1. Ändra metodens deklaration, namnet, ingen inparameter och inget returvärde.
  2. Ändra kommentaren för metoden.
  3. Ändra lista vi jobbar mot.
  4. Vi har ett anrop till printList() som inte funkar. Därför måste vi börja med att ändra metoden printList().
    • Ändra metodens deklaration, ingen inparameter och kommentarer för printList().
    • Ändra listan som används.
    • Kanske se över utskriften i printList(), eftersom den nu skall hämta information om olika objekt (Car vs Lorry) vilket kan påverka utskrifterna.
    • Andra anrop till printList() kommer nu bli felaktiga. Leta upp det som finns i switch-satsen under case '1':
  5. Inte returnera listan i metoden removeVehicle().
  6. Ändra anropet från switch-satsen.

Hittade lite felaktigheter

När jag gjorde detta så såg jag att min meny inte såg ut som den borde vilket jag ändrade. Jag hade skrivit ut två alternativ nr 3, och dessutom ta bort bil när jag skulle göra val i menyn.

Saker som man upptäcker under tiden, det är därför vi skall planera våra projekt så noggrant som möjligt för att inte behöva ändra, ändra, göra om, ändra och rätta i all oändlighet. Innan vi är helt klara sedan så skall vi se till att gå igenom hela applikationen och testa den rejält.

Nu var det lite mer att göra men prova gärna att lösa dessa ändringar på egen hand innan du kikar på koderna som du hittar nedan.

Gömda koder [klicka för att visa]

Metoden removeVehicle()

Metoden printList()

Anropet från switch-satsen

Kompilera och testa att det går att ta bort ett fordon.

Det blev lite extra rörigt eftersom våra metoder var kopplade till varandra. Men det kändes ändå ganska bra då metoderna inte var allt för stora vilket gjorde det hanterbart. Extra skönt blev det ju nu att vi kunde göra om anropet till metoden som skriver ut hela listan som vi ändå använder i vårt menyval nr 1. Smidigt med metoder som används på flera olika sätt. Jag är inte helt nöjd med utskriften av listan eftersom vår medlemsmetod ToStringList() skriver ut olika saker eftersom det lagras olika saker för de olika objekten. Att lösa detta för dessa två klasser, Car och Lorry, kanske inte är någon större mening då vi senare kanske skulle vilja lägga till fler klasser som har unika egenskaper. Det kanske hade räckt att lägga till en kolumn med rubriken Övrig info och där lagra den info som behövs. Det kanske inte behöver visas upp all info om ett fordon ifall det är väldigt mycket. Istället kanske det hade varit läge att använda medlemsmetoden ToString() istället för att skriva ut all info om ett specifikt objekt på ett annat sätt än i en lista.

Den funktionaliteten kommer inte jag skapa i denna tutorial men du får gärna bygga den själv, jag tror inte att ni kommer tycka att det är allt för svårt och troligtvis hade det varit rimligt att bygga på den under menyval 1 där vi listar alla objekt och sedan får användaren välja ett index som vi gör när vi tar bort ett fordon men istället visa upp all info om det valda fordonet. Där har ni en lösning, implementera den gärna för att träna på egen hand.

3.7 emptyList()

Nästa sak att ändra är metoden emptyList() som just nu tömmer fel lista.

Nu har vi ändrat den typen av metoder flera gånger så den tror jag att du kan ändra utan hjälp.

Gömda koder [klicka för att visa]

Metoden emptyList()

Anropet från switch-satsen

Kompilera och testa att listan töms.

4. Testa och snygga till koden

Nu borde vi ha en fungerande applikation, vi har ändrat alla delprogram, vi har testat varje del för sig och det är nu dags att testa helheten. När vi testar allt så hade det varit smidigt att ha ett testprotokoll så att vi inte missar någon del som skall testas, men för oss i denna applikationen så borde det räcka att testa alla delprogram en gång till. Vi har ju inte ändrat i någon av klasserna Vehicle, Car eller Lorry i denna tutorial så det som finns där borde vi inte behöva röra.

4.1 Ta bort överflödiga variabler och metoder

Ta bort allt som har med carList att göra, enklast är att söka efter det och när du inte hittar några fler träffar så är du klar.

Ta också bort de metoder som inte längre behövs. Flera metoder har vi skrivit om men vi har också byggt en ny metod, addVehicleAtStart() som skulle ersätta addCarAtStart() som vi nu kan ta bort.

4.2 Rensa och rätta kommentarer

Läs igenom alla kommentarer i vår fil Program.cs så att dessa stämmer. Vi har ändrat en del framförallt i våra listor så det finns risk att vi kommenterat om den gamla listan carList och det ser inte så snyggt ut.

4.3 Testa all funktionalitet

Testa igenom hela applikationen, avbryt inte på vägen för att ändra något i koden utan kör igenom hela applikationen genom alla menyval och anteckna de saker som du inte tycker stämmer. Läs extra noga på ledtexter så att dessa stämmer.

Rätta sedan det som eventuellt behöver rättas och testa om hela applikationen en gång till. Det är smidigt att skapa ett testprotokoll vilket innebär att du innan du testar din applikation har skrivit exakt vad du skall testa och hur varje del skall testas så att du testar det på samma sätt varje gång. Då vet du att du inte missar någon del.

Byt gärna tester med din kompis, hen får testa din applikation mot att du får testa kompisens applikation. Skriv ner allt som du inte förstår, är otydligt eller är direkt felaktigt.

4.4 Snygga till koden

En sista okulärbesiktning av koden för att se att den är snygg och att allt verkar ligga på rätt ställe, har korrekt indentering och att alla bortkommenterade kodavsnitt som gärna blir liggande tas bort.

4.5 Påpekanden från Visual Studio

Det finns två saker som Visual Studio påpekar i mitt sätt att koda just nu. Det ena är en namning rule violation på mina metoder som Visual Studio vill att jag skall skriva med versal i början av metodens namn, addCar() skall istället skrivas AddCar(). Jag kan köpa argumentet men jag är i grunden en javautvecklare och där skriver vi enligt standarden camelCase vilket gör att jag skriver på det sättet. Skall jag strikt följa namnregler för C# så bör jag skriva enligt PascalCase. Ändra om ni vill, jag låter det vara just nu för att inte röra till det mer än nödvändigt (det är först i denna version av Visual Studio som jag har fått detta påpekande).

Nästa sak som inte följer namnregler för C# är att jag inte kommenterar metoder på rätt sätt. Det enklaste är att i Visual Studio skriva tre (3) slash (///) precis ovanför en metod för att få hjälp att dokumentera denna. Fördelen om man använder denna tekniken är att Visual Studio senare skriver ut den info som jag själv har angivit om jag vill anropa en metod från en annan klass. Detta är extra användbart om jag t.ex. använder flera metoder som överlagrar varandra eller som heter nästan samma sak men gör lite skillnader i funktioner. Då hjälper texten mig att välja rätt metod. Inte heller detta ändrar jag nu, mest för att det är en del jobb men också där för att inte förvirra dig (och mig) allt för mycket. Hade jag gjort om uppgiften idag så hade jag dock dokumenterat enligt detta sättet.

5. Hela applikationens kod

Eftersom jag endast har jobbat i filen Program.cs så är det endast den som jag visar upp. Alla klasser som har med Vehicle, Car och Lorry att göra kan du hitta i tidigare tutorial.

5.1 Program.cs