Tutorial - OOP C# Samling

1. Introduktion

I den tidigare tutorialen OOP C# grund skapade vi klassen Car och på det sättet lärt oss att jobba med objekt. Nu skall vi arbeta vidare med klassen Car och se till att vi kan lagra, lista och ta bort bilar (Car-objekt) från en samling.

Om du har ett projekt med hela den tidigare tutorialen så skapa en kopia av denna och jobba vidare. Det är alltid smart att spara applikationer som fungerar, du vet aldrig när du behöver, eller vill, återanvända detta senare.

Har du inte gjort den tidigare tutorialen så behöver du iaf gå igenom klassen Car, den koden ligger i slutet av den första tutorialen, för att kunna följa med här. Själva programmet och den kod som styr applikationen kommer vi till stor del att skriva om.

Vi skall bygga en applikation där vi kan lagra objekt av typen Car i en samling. Vi kommer göra applikationen menybaserad, se bilden nedan, och alla menyalternativ kommer ligga i egna metoder. Vår målsättning är att skriva en snygg applikation som är lätt att arbeta med och som lätt kan återanvändas till andra projekt.

bild

Note

Bilderna i denna tutorial kommer från Xamarin vilket är en IDE för Mac. Om du kör på en dator med Windows så kommer det inte se likadant ut, men det bör inte vara några problem att genomföra denna tutorial i Visual Studio.

Buggar

C# är ett levande språk som trots att vi bara jobbar med grunderna fortfarande kan vålla oss lite problem och spela oss små spratt emellanåt. Långt ner i denna tutorial, strax före hela koden publiceras, så kan det finnas info och exempel på saker som kanske inte fungerar i en viss miljö, Windows/MacOS, eller saker som vi hittar som inte längre fungerar som det är tänkt.

1.1 Att tänka på

Denna tutorial arbetar med samlingstypen List, det finns flera likheter med andra samlingar och stora delar av denna kod skulle vara identisk eller liknande om man valde att använda samlingarna; Stack, Queue eller någon annan. Varje samlingstyp har sina för och nackdelar och det finns flera bra metoder som inte används i denna övning. Tanken är att göra uppgiften generell så att du som användare i första hand lär sig principerna, det går alltid att skriva bättre och/eller annan kod och ibland kan det vara att föredra andra lösningar.

2. Skapa projekt

Börja med att skapa ett nytt projekt som du döper till något lämpligt, jag valde CarDemoCollection. Det första jag då gjorde var att kopiera in Car.cs och Program.cs från vårt tidigare projekt. Det funkar lika bra att duplicera det gamla projektet, byta namn på mappen och öppna detta projekt med Xamarin. Klassen Car kommer vi iaf till en början inte ändra så mycket i men Program.cs kommer vi i stort sett skriva om från början så den skulle lika gärna kunna vara tom.

Testkör din applikation så att den fungerar, om du kör den gamla Program.cs, eller att den inte ger några error vid kompilering om du kör en tom Program.cs.

3. Deklarera List som en medlemsvariabel

I filen Program.cs så skall vi nu deklarera vår samling som en medlemsvariabel. Det gör du genom att skriva följande kod;

kod

Nu har vi skapat samlingen som vi sedan skall använda för att placera alla systemets bilar i. Vi behöver inte testa detta nu.

4. Skapa menyn

Det första vi skall göra är att skapa vår meny, utifrån menyn skall vi sedan styra användaren till olika metoder, beroende på vad hen vill göra i vår applikation. Vi låter hela applikationen vara metodstyrd, vilket innebär att varje litet delprogram är en egen metod. Detta gör programmet enklare att följa, då varje metod inte blir så stor, det gör det också enklare att låta flera utvecklare vara med och bygga ett program.

Vi börjar bygga metoden menu i filen Program.cs

kod

Här kan vi notera några saker;

  • Kika lite på hur jag kommenterar metoderna, det är vitkigt att kommentera för din egen skull men gör det också till en van för att någon anan skall kunna gå in och rätta, eller fortsätta utveckla, din kod. I C# så finns det ingen dokumentationsstandard jämförbart med JavaDoc som används i Java men det gör det ändå inte frivilligt.
  • I metoden menu så görs ingen koll att användaren har valt något av de möjliga alternativen för menyn. Det går naturligtvis att lägga till men vi kommer göra viss kontroll när vi skall behandla användarens svar.
  • Det finns flera olika tekniker att använda sig av när vi skall ta emot ett värde, jag har valt att använda metoderna ReadKey som läser av en enda nedtryckning på tangentbordet och KeyChar som gör om det till en Char.

5. Skapa applikationens huvudloop

I main-metoden skriver vi nu loopen som sköter huvudlogiken i vår applikation.

kod

Denna kod borde inte innehålla några konstigheter.
På rad 15 anropas metoden menu() som ritar ut menyn och returnerar ett char-värde som lagras i menuSelection och som det sedan görs en switch-selektion på för att välja vilket av de mindre delprogrammen som skall köras. Loopen kommer att köras så länge användaren inte matar in 0. Jag valde do-whileloopen eftersom den alltid skall köras minst en gång och det är smidigt att först gå in i loopen och sedan fråga efter det val som användaraen vill göra.

Testa att köra applikationen om du vill, det enda som kommer hända är att menyn kommer att ritas ut oavsett vad du väljer att mata in för alternativ.

6. Applikationens metoder

Nu är det dags att titta på de metoder som användaren anropar via menyn för att arbeta med applikationen, i mina kommentarer kommer jag fokusera på de delar som rör vår lista, övrig kod bör inte vara någon nyhet.

I denna applikation så har vi inte skapat listan som en global variabel, på ett sätt hade detta varit enklare, då hade vi inte behövt skicka in listan som inparameter till alla våra metoder och sedan returnera listan när vi är klara med metoden. Anledningen till att jag har valt att inte göra listan global är dels för att slippa blanda variabler som är static och variabler som inte är static, dels för att om man börjar skapa globala variabler så blir det lätt att man fortsätter med det och då slutar det ofta med att det finns en hel mängd variabler som lagras som globala trots att det inte finns någon som helst anledning till detta. Kanske återkommer jag i slutet av denna tutorial till hur man skulle kunna göra om listan vore global, kanske inte.

Alla metoder skall skrivas i Program.cs. Tänk så här, det som tillhör programmet skrivs i programmets fil, det som är specifikt för objektet, i detta fall Car, skrivs i klassen Car.

6.1 Skapa addCar

kod

Vad händer i denna metoden? I metoddeklarationen (på rad 42) anger vi att det som returneras från metoden är en List som lagrar objekt av typen Car. Vi anger också att det skall skickas in en lista av samma typ och den variabeln döper vi till carList.

Inmatningen av en ny bil är samma som tidigare så här sker inget nytt. Det nya är att använda metoden Add() till listan som helt enkelt lägger till objektet sist i listan. Slutligen returnerar vi listan och då måste vi se till att vi tar emot den när vi anropar metoden. Den koden ser ut så här;

kod

Bra. Nu kan vi lägga till en bil till en lista men vi kan fortfarande inte se att det finns några bilar lagrade i vårt system.

Vi provar att modifiera menyn så att det står hur många bilar det finns i systemet.

6.2 ändra menu

Vi börjar med att ändra metoden menu.

kod

På rad 81 så ändrar vi i metoddeklarationen så att metoden får carList som inparameter. Glöm inte att ändra detta i metodens dokumentation. På rad 87 så skrivs listans antal objekt ut. Jag har också snyggat till menyn lite, men det är endast kosmetiskt. Eftersom vi inte påverkar carList på något sätt inne i metoden så finns det ingen anledning att returnera denna.

I metoden main så behöver vi nu ändra anropet till metoden menu och även skicka med carList.

kod

Kompilera och testkör applikationen.

kod

Härligt!

6.3 Skapa metoden printList

Vi kommer att testa applikationen en hel del gånger i detta och följande avsnitt. Om du, som jag tycker, att det är småtrist att gång på gång mata in bilar och vara försiktig så att inte applikationen kraschar så kan tipset att skapa en metod som alltid skapar tre bilar när applikationen körs.

Nu skall vi skapa metoden för att skriva ut en lista med bilar. Vi har bara en medlemsmetod, i klassen Car, som skriver ut information om en bil. Vi testar att använda denna för att se hur det skulle se ut att använda denna.

kod

Anropet från huvudprogrammet skriver vi så här;

kod

Utskriften blir då;

kod

Nej, så kan det inte se ut. Vi måste skapa en annan medlemsmetod i klassen Car för att skriva ut listan.

Vi gör det enkelt för oss, vi kopierar hela metoden toString() klassen i Car.cs och sedan modifierar vi den metoden. Jag döper den först till toStringList() och så här skriver jag metoden, du får gärna skriva den annorlunda.

kod

UML Glöm inte att när vi har uppdaterat klassen så behöver vi också uppdatera vår UML.

Det gick att använda samma kod som redan fanns för ToString förutom när jag ville få reda på om en bil var till salu eller inte. Den metoden, ForSaleToString(), som användes gav mig ett värde som inte gik att använda. Jag valde att inte skapa en ny getmetod utan att använda propertyn ForSale och göra en selektion på det värdet. Det går att göra annorlunda ohc man kan alltid diskutera det bästa sättet, denna lösning fungarar utmärkt. Om jag misstänker att jag vid något annat tillfälle kommer behöva använda JA/NEJ som info om att en bil är till salu bör jag göra en andra get-metod för att hämta detta värde, denna gång lät jag bli.

Då skriver jag också om printList-metoden i Program.cs så att listan skrivs ut snyggt. Här blir det en del formatering, jag har också lagt till en räknare som visar positionen för varje bil i listan.

kod

Utskriften ser då ut så här;

kod

På utskriften ser vi att det är uppenbar risk att ett långt namn på en modell eller ett bilmärke saboterar vår lista. Det kan vi lägga ner väldigt mycket tid för att jobba fram olika lösningar beroende på vad som händer. Men just nu är vi nöjda med detta resultat.

6.3.1 Tips: Läs in bilar vid programstart

Eftersom vi testar en hel del har jag skapat en metod som skapar tre bilar vid uppstarten av applikationen, använd den om du vill. Metod och anrop ser ut så här;

kod kod

Här väljer jag att inte skicka med den tomma listan carList till metoden addCarsAtStart(), istället skapar jag en lokal lista i metoden som sedan returneras. Är det smidigare eller smartare att göra på det sättet? Det går att diskutera men vi kan se det som ett alternativt sätt att göra det på.

6.4 Skapa metoden removeCar

Dags att ge användaren möjlighet att ta bort en bil ur listan. Metoden skall ligga i Program.cs

kod

Här återanvänder vi en metod som vi skapade tidigare, det är den som skriver ut listan av bilar. Vi ger användaren chansen att ange 0 för att slippa ta bort en bil, denna kontroll görs på rad 150.

Tänk på att numreringen i listans utskrift är 1, 2, 3 osv medan index i listan carList är 0, 1, 2 osv, därför måste vi på rad 152 ta bort objekt med index-1.

Ingen felhantering finns i detta läge, om användaren matar in ett felaktigt värde, inte heltal, eller ett index som ligger utanför de index som är representerade i listan så kommer applikationen att krascha. Hur du som utvecklare skyddar dig mot detta kommer vi kika på senare i kursen.

Innan vi kan testa om vår metod fungerar måste vi göra ett anrop ifrån vårt huvudprogram, det anropet ser du på rad 31.

kod

Så, nu kan vi testa. Då borde resultatet bli detta.

kod

6.5 Skapa metoden emptyList

Dags att skriva metoden för att tömma listan;

kod

Anropet från huvudprogrammet blir då;

kod

Resultatet blir då!

kod

Klart!

7. Vidareutveckling

När jag kikar igenom koden så ser jag att jag använder versaler som första bokstav i metoderna i klassen Car men gemen begynnelsebokstav i klassen Program. Så skall det naturligtvis inte se ut utan det får jag ändra. Orsaken till detta är att jag är mer van att koda Java och php än C# och där är det en annan namngivingsstandard som gäller. Det har sina nackdelar att hoppa mellan språk.

Hur kan man vidareutveckla denna applikation?

  • Skapa en global variabel för att enklare kunna jobba med listan? För och nackdelar med detta?
  • Ge användaren möjlighet att upprepa ett visst delprogram, alltså utan att hoppa tillbaka till det stora huvudprogrammet få möjlighet att lägga till flera bilar, ta bort flera bilar osv.
  • Det finns ingen kontroll för validerat data, det borde läggas dit för att skapa en bra applikation. En applikation som kraschar är aldrig trevlig för användaren.
  • Vi skulle kunna skapa någon form av metod som ger bättre information om vad som är gjort, t.ex. ”En bil är tillagd/borttagen”, ”Listan är tömd”, antingen i direkt anslutning till menyn eller i ett läge där användaren får info och sedan godkänner med ”enter” innan menyn skrivs ut och applikationen fortsätter.
  • Avsluta applikationen på ett trevligt sätt.
  • Sista steget för att få en ”riktig” applikation är att kunna spara bilarna som skapats i en fil så att listan kan läsas in nästa gång applikationen öppnas. Kanske kommer vi tillbaka senare och fixar detta.

8. Buggar

Bug: Console.Read()

Inmatning av data för att ange att en bil är till salu J/N kan orsaka problem med Console.Read(). Detta verkar främst gälla i Windows.

Problemet visar sig när användaren skall mata in en andra bil, under samma körning, då kommer användaren inte få möjlighet att mata in registreringsnummer utan dialogen hamnar direkt i inmatning av bilmärke. Orsaken till detta är att Console.Read() inte gör sig av med allt innehåll utan något som liknar ett enter ligger kvar och väntar på nästa inmatning.

Lösningen är att byta ut

char ch = Convert.ToChar(Console.Read());
mot
char ch = Convert.ToChar(Console.ReadKey().KeyChar);

9. Hela applikationens kod

9.1 Program.cs

kod kod kod kod

9.2 Car.cs

kod kod kod kod