hjälp med c# kod! [hur jag ska få in är underklasser till min klass?]

Permalänk
Medlem

hjälp med c# kod! [hur jag ska få in är underklasser till min klass?]

Jag har fastnat på en uppgift i min kurs i c# och blir galen. Jag vet inte hur jag ska koppla samman min kod.
Jag har byggt upp som ett bibliotek där folk kan välja att antingen lägga till en bok, kolla vilka böcker som lagts till, söka efter en bok eller gå ur programmet. Det jag inte förstår hur jag ska få in är underklasser till min klass som är ett krav.
När man går in i menyn och väljer att man vill lägga till en bok så får man nu upp "ange författare" "ange titel" och sista ska vara ett val istället mellan 3 olika typer av böcker tex: 1. roman 2. tidning 3. novell. Allt detta ska sedan sparas och kunna listas upp.
Huvudklassen är bok.
Jag ska skapa 3 underklasser, en för varje typ av bok.
Har suttit och prövat lite olika saker tex array osv, men jag känner bara att jag förstår inte vad jag håller på med.

Min kod ser ut såhär just nu:

using System; using System.Collections.Generic; class MainClass { static void Main() { BookManager manager = new BookManager(); manager.Menu(); } } public class Book { public static int NumberOfBooks; private string title; private string author; private string typ; public Book(string title, string author, string typ) { this.title = title; this.author = author; this.typ = typ; NumberOfBooks++; } public string GetTitle() { return title; } public string GetAuthor() { return author; } public string GetTyp() { return typ; } public void SetTitle(string title) { this.title = title; } public override string ToString() { return title + " " + author + " " + typ; } } public class Tidskrift : Book { public string Tidskrift() { typ = "Tidskrift"; } } public class Roman : Book { public Roman() { typ = "Roman"; } } public class Novellsamling : Book { public Novellsamling() { typ = "Novellsamling"; } } public class BookManager { private List<Book> library; public BookManager() { library = new List<Book>(); } public void Menu() { bool loop = true; while (loop) { int choice = GetMenuChoice(); switch (choice) { case 1: AddBook(); break; case 2: ListBooks(); break; case 3: SearchByTitle(); break; case 4: loop = false; break; default: Console.WriteLine("Fel inmatning, pröva igen!"); break; } } } private int GetMenuChoice() { Console.Write("Välkommen till Biblioteket!\n"); ; Console.WriteLine("1. Lägg till en bok"); Console.WriteLine("2. Lista över tillagda böcker"); Console.WriteLine("3. Sök efter titel"); Console.WriteLine("4. Avsluta"); Console.Write("\nvad vill du göra? : "); string choice = Console.ReadLine(); int numChoice = Convert.ToInt32(choice); return numChoice; } private void AddBook() { Console.Clear(); Console.WriteLine("Titel: "); string title = Console.ReadLine(); Console.WriteLine("Skribent: "); string author = Console.ReadLine(); Console.WriteLine(här ska valet mellan typ av bok vara); string typ = Console.ReadLine(); Console.Clear(); Book b = new Book(title, author, typ); library.Add(b); } private void ListBooks() { Console.Clear(); foreach (Book b in library) { Console.WriteLine(b.ToString() + "\n"); } } private void SearchByTitle() { Console.Write("Titel: "); string titel = Console.ReadLine(); foreach (Book b in library) { if (titel == b.GetTitle()) { Console.WriteLine(b.ToString() + "\n"); } } } }

Fixat Code-taggar, rubrik förtydligad /screamin-daemon, moderator
Permalänk
Hedersmedlem

Hej!

Redigera ditt inlägg och släng in [code] före din kod och [/code] efter. Då blir indenteringen (indragen) korrekt och det blir mycket lättare att läsa.

Permalänk
Hedersmedlem

Tack, @Kent för att du redigerade inlägget så det gick att läsa!

Du har kommit en bra bit på vägen. Du har definierat subklasserna Tidskrift, Novellsamling och Roman till Book. Men det du saknar är korrekta konstruktorer. Du behöver skapa en konstruktor t.ex. Tirskrift(string title, string author) som i sin tur anropar basklassens (Books) konstruktor. Osäker på hur du gör det i C#. Hade det varit Java hade du behövt skriva typ detta längst upp i konstruktorn:

super(title, author, "Tidskrift")

Läs på om konstruktorer i subklasser i C# så klurar du säkert ut exakt vad du ska göra! Verkar som att det du behöver i C# är att använda ordet "base", kolla här så får du se: https://docs.microsoft.com/en-us/dotnet/csharp/programming-gu...

Det du sedan behöver göra är att ha ett sätt att skapa objekten på. Du kan t.ex. kopiera din funktion som heter AddBook() i BookManager, och skapa olika versioner för att lägga till tidskrifter eller novellsamlingar etc, eller så kan du ha ett val i slutet där man får välja typ, och beroende på om det är en tidning eller novellsamling så skapas allså rätt bok. Något i stil med följande pseudokod:

val = fråga("Vilken sorts bok ska vi lägga till?") if val == "Tidskrift": book = new Tidskrift(title, author) elif val == "Novellsamling": book = new Novellsamling(title, author) else: print "Ogiltigt val. Hej då!" return library.add(book)

Notera alltså att din Library fortfarande kan vara en List<Book>. I den kan du lägga in allt som är en subklass till Book, och du kan anropa Books metoder även om objektet faktiskt är t.ex. en Roman istället. Det är det som är lite grejen med subklasser och arv (inheritance).

Permalänk
Avstängd

@Missyym: @pvb har ganska rätt i vad han säger men du gör ju mycket av detta redan liksom. Ett tips är att du ändrar i din AddBook-metod så att du inte skapar en ny Book utan en ny Tidskrift eller vad det nu är för typ. Sen är det väl inte jättesnyggt att ha typen som en string då det krävs rätt stavning och så, jag hade nog gjort en enum istället och presenterat vilka alternativ det finns och sen låtit användaren välja ett av dem genom att mata in en siffra eller så.

Permalänk
Hedersmedlem
Skrivet av snajk:

@Missyym: @pvb har ganska rätt i vad han säger men du gör ju mycket av detta redan liksom. Ett tips är att du ändrar i din AddBook-metod så att du inte skapar en ny Book utan en ny Tidskrift eller vad det nu är för typ. Sen är det väl inte jättesnyggt att ha typen som en string då det krävs rätt stavning och så, jag hade nog gjort en enum istället och presenterat vilka alternativ det finns och sen låtit användaren välja ett av dem genom att mata in en siffra eller så.

Om vi ska pratar förbättringar: Egentligen behöver inte själva typen lagras i ett datafält i själva klassen heller, utan istället så kan man använda polymorfism och ha olika implementationer för en polymorfisk metod med namnet t.ex. getBookType().

Självklart så förespråkar jag inte heller att användaren ska behöva stava rätt på "Novellsamling", när han ska skapa en sådan, utan det var mest tänkt att visa som en illustration att man beroende på utfallet i en if-sats anropar olika subklassers konstruktorer och sedan sparar objektet oavsett typ i en List<Book>.

Permalänk
Avstängd
Skrivet av pv2b:

Om vi ska pratar förbättringar: Egentligen behöver inte själva typen lagras i ett datafält i själva klassen heller, utan istället så kan man använda polymorfism och ha olika implementationer för en polymorfisk metod med namnet t.ex. getBookType().

Självklart så förespråkar jag inte heller att användaren ska behöva stava rätt på "Novellsamling", när han ska skapa en sådan, utan det var mest tänkt att visa som en illustration att man beroende på utfallet i en if-sats anropar olika subklassers konstruktorer och sedan sparar objektet oavsett typ i en List<Book>.

Jo, jag menade inte att skriva dig på näsan eller så. Bara att grejerna med arv är redan lösta, det behövs inte så mycket i C# som i Java för att det ska funka och utvecklingsmiljön (om det är VS i alla fall) ger väldigt mycket gratis. Förbättringen var bara det första jag såg.

Permalänk
Medlem

Bra tips lämnade redan, precis som pv2b nämner behöver du anropa baskonstruktorn i dina underklasser, eftersom enbart basklassen book vet hur du vill lagra titel/skribent, och när du sen skapar böcker av dessa undertyper skapar du dem undertyperna direkt,
dvs. ".... = new Roman(titel, skribent)", notera att värdet boktyp inte behövs i detta fallet eftersom det fylls i automatiskt av underklassens konstruktormetod.
En av tankarna med övningen är att man lär sig hur klassarv fungerar i praktiken.

Sen håller jag med snajk om att enum och en simpel switch för att välja boktyp vid skapandet av ny bok är mycket smartare och betydligt mindre risk för att generera fel vid inmatning.

Permalänk
Medlem

@Missyym: Du har råkat definierat ListBooks och SearchByTitle innuti AddBook, kolla hur alla { och } ligger efter switch-satsen i AddBook.

Permalänk
Medlem

tack! en sista fråga då.... hur hindrar jag mitt program att krascha om användaren skriver in en bokstav istället för en siffra?
har redan fixat så att det kommer ett meddelande om man väljer en siffra ur menyn som inte finns, men vid bokstav så kraschar allt... vet att man kan använda TryParse men vet inte vart jag ska få in det för hur jag än gör så blir hela koden fel när jag försöker.

using System; using System.Collections.Generic; public class MainClass { static void Main() { BookManager manager = new BookManager(); manager.Menu(); } } public class Book { public static int NumberOfBooks; public string title; public string author; public string typ; public Book(string title, string author, string typ) { this.title = title; this.author = author; this.typ = typ; NumberOfBooks++; } public string GetTitle() { return title; } public string GetAuthor() { return author; } public string GetTyp() { return typ; } public void SetTitle(string title) { this.title = title; } public override string ToString() { return title + " " + author + " " + typ; } } public class Tidskrift : Book { public Tidskrift(string title, string author, string typ) : base(title, author, typ) { typ = "Tidskrift"; } public override string ToString() { return "bokens namn är " + title + " den är skriven av " + author + " och av boktypen " + typ + ". Tidskrifter finns på hylla T."; } } public class Roman : Book { public Roman(string title, string author, string typ) : base(title, author, typ) { typ = "Roman"; } public override string ToString() { return "bokens namn är" + title + " den är skriven av " + author + " och av boktypen " + typ + ". Novellsamlingar finns på hylla N."; } } public class Novellsamling : Book { public Novellsamling(string title, string author, string typ) : base(title, author, typ) { typ = "Novellsamling"; } public override string ToString() { return "bokens namn är " + title + " den är skriven av " + author + " och av boktypen " + typ + ". Novellsamlingar finns på hylla N."; } } public class BookManager { private List<Book> library; public BookManager() { library = new List<Book>(); } public void Menu() { bool loop = true; while (loop) { int choice = GetMenuChoice(); switch (choice) { case 1: AddBook(); break; case 2: ListBooks(); break; case 3: SearchByTitle(); break; case 4: loop = false; break; default: Console.WriteLine("Fel inmatning, pröva igen!"); break; } } } private int GetMenuChoice() { Console.Write("Välkommen till Biblioteket!\n"); Console.WriteLine("1. Lägg till en bok"); Console.WriteLine("2. Lista över tillagda böcker"); Console.WriteLine("3. Sök efter titel"); Console.WriteLine("4. Avsluta"); Console.Write("\nvad vill du göra? : "); string choice = Console.ReadLine(); int numChoice = Convert.ToInt32(choice); return numChoice; } private void AddBook() { Console.Clear(); Console.WriteLine("Titel: "); string title = Console.ReadLine(); Console.WriteLine("Skribent: "); string author = Console.ReadLine(); Console.WriteLine("Vilken sorts bok ska vi lägga till? "); Console.WriteLine("1. Roman"); Console.WriteLine("2. Tidskrift"); Console.WriteLine("3. Novellsamling"); string typ = Console.ReadLine(); int bokChoice = Convert.ToInt32(typ); switch (bokChoice) { case 1: { Book b = new Roman(title, author, "roman"); library.Add(b); break; } case 2: { Book b = new Tidskrift(title, author, "tidskrift"); library.Add(b); break; } case 3: { Book b = new Novellsamling(title, author, "novell"); library.Add(b); break; } } } private void ListBooks() { Console.Clear(); foreach (Book b in library) { Console.WriteLine(b.ToString() + "\n"); } } private void SearchByTitle() { Console.Write("Titel: "); string titel = Console.ReadLine(); foreach (Book b in library) { if (titel == b.GetTitle()) { Console.WriteLine(b.ToString() + "\n"); } } } }

Permalänk
Medlem

åhnej. såg att man kan skriva in siffror och allt möjligt i Addbook() också utan att programmet reagerar. tips på korrigering?

Permalänk
Medlem

Ser bra ut.

Angående TryParse

Dessa rader byter du ut:
int numChoice = Convert.ToInt32(choice);
samt
int bokChoice = Convert.ToInt32(typ);

Till t.ex.

int numChoice; if (int.TryParse(choice, numChoice)) { // switchblock här; } else { // printa felaktig inmatning }

TryParse returnerar nämligen ett boolvärde (true/false) om den lyckas eller inte lyckas konvertera till heltal, men lagrar samtidigt (om den lyckas) heltalet i den andra argumentvariabeln, annars lagrar den 0 (har jag för mig).

Permalänk
Medlem

@elBenko: Tack för tipset! men det funkar inte för mig, prövade ändra men får massa felmeddelanden i min kod, exakt hur ska jag bygga om min kod för att få in TryParse? jag fattar verkligen ingenting. kan jag få ett kodexempel baserat på min kod jag har skrivit?

Permalänk
Medlem
Skrivet av Missyym:

@elBenko: Tack för tipset! men det funkar inte för mig, prövade ändra men får massa felmeddelanden i min kod, exakt hur ska jag bygga om min kod för att få in TryParse? jag fattar verkligen ingenting. kan jag få ett kodexempel baserat på min kod jag har skrivit?

Kan ge ett exempel på AddBook metoden:

private void AddBook() { Console.Clear(); Console.WriteLine("Titel: "); string title = Console.ReadLine(); Console.WriteLine("Skribent: "); string author = Console.ReadLine(); Console.WriteLine("Vilken sorts bok ska vi lägga till? "); Console.WriteLine("1. Roman"); Console.WriteLine("2. Tidskrift"); Console.WriteLine("3. Novellsamling"); string typ = Console.ReadLine(); int bokChoice; if (int.TryParse(typ, bokChoice)) { switch (bokChoice) { case 1: { Book b = new Roman(title, author, "roman"); // "roman" behövs ej här eftersom underklassens konstruktor sätter värdet åt dig library.Add(b); break; } case 2: { Book b = new Tidskrift(title, author, "tidskrift"); // samma gäller här library.Add(b); break; } case 3: { Book b = new Novellsamling(title, author, "novell"); // och här library.Add(b); break; } default: { // om inmatning är godkänt heltal men inte 1-3 så printar vi "felaktigt menyalternativ" här. } } } else { // om inmatning är icke-godkänt heltal så printar vi "felaktig inmatning" här. } }

Permalänk
Medlem

Om jag inte minns fel så är int.TryParse andra parametern en out variabel och måste därför definieras vid anrop.
Alltså

int numChoice; if (int.TryParse(choice, out numChoice)) { // switchblock här; } else { // printa felaktig inmatning }

Permalänk
Medlem
Skrivet av NFRS:

Om jag inte minns fel så är int.TryParse andra parametern en out variabel och måste därför definieras vid anrop.
Alltså

int numChoice; if (int.TryParse(choice, out numChoice)) { // switchblock här; } else { // printa felaktig inmatning }

Japp, helt rätt, inte pysslat med c# på ett tag så jag missade detta.

Permalänk
Medlem

input plz!

Super tack för alla tips! Jag fick ihop koden så bra!! blev super nöjd!
Nu ska jag börja bygga med windows forms som jag aldrig tidigare har arbetat med. Har suttit hela dagen med detta projekt men har fastnat och nu är min hjärna köttfärs. Jag har kvar min Bok-klass och underklasserna till den men har inte lagt med den koden i det här stycket för det hade blivit så långt, men den finns ju ovan...

Saker jag måste göra:
*läsa in en textfil rad för rad i en lista (texten i min textfil är författare###titel###boktyp###true/några är false) (gjort)
*plocka ut varje sträng från listan och separera strängen till strängarray (gjort)
*spara strängarray i en strängarraylista (har jag gjort rätt? hur gör man en vektorlista<string[]>?)
*skapa en klass som importerar data från textfilen till sträng (gjort) och sparar dom som böcker av rätt underklass i en lista (va?)
*använd sparade värden i arrayerna för att skapa böcker (gjort?)
*spara böcker i boklista - använd boklista för att slumpa boktips. ?
*böckernas ToString-override ska kunna visa om boktipset är tillgängligt eller inte (pga innehåller true/false, men hur använder man det?)

Det här är koden jag har skrivit nu:

namespace WindowsFormsAppslump { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } private void textBox1_TextChanged(object sender, EventArgs e) { // här ska tipsen skrivas ut när man tryckt på slumpknappen } private void button1_Click(object sender, EventArgs e) { Random rnd = new Random(); // här ska tipsen slumpas fram när man trycker } } public class FileLoader { public FileLoader() { var itemSaver = new List<string>(); //skapar ny lista där mina böcker ska sparas if (File.Exists("")) //- om textfilen finns eller inte {//läser in min textfil StreamReader reader = new StreamReader("", Encoding.Default, true); string book = ""; while ((book = reader.ReadLine()) != null)) { itemSaver.Add(book); //lägger till bok i listan } foreach (string b in itemSaver) //för varje sträng i listan: { string[] vektor = b.Split(new string[] { "###" }, StringSplitOptions.None); //splittra strängarna och lägg dom i en array. här måste jag göra en vektorlista(?): List<Book> RigList = new List<Book>(vektor); //som sorterar böckerna efter title, author och typ som jag har i min bok-klass: List<Book> sortedList = RigList .OrderBy(r => r.title) .ThenBy(r => r.author) .ThenBy(r => r.typ) .ToList(); // när man slumpar värdena i vektorlistan så ska dom alltid skrivas ut "titel, author, typ" }

Permalänk
Medlem

@Missyym:

Citat:

*spara strängarray i en strängarraylista (har jag gjort rätt? hur gör man en vektorlista<string[]>?)

string[] myVector = new string[] { "a", "b", "c" }; // skapar strängvektorn List<string[]> myList = new List<string[]>(); // skapar en lista av typen strängvektor myList.Add(myVector); // adderar vektorn till listan

Citat:

*skapa en klass som importerar data från textfilen till sträng (gjort) och sparar dom som böcker av rätt underklass i en lista (va?)

vad som efterfrågas, som jag förstår det, är en klass som
1. läser in textfilen.
2. sparar varje rad temporärt, delar raden och sparar de respektive delarna för raden i en strängvektor.
3. lagrar varje strängvektor i en lista av typen strängvektor (se ovan).
4. konverterar varje lagrad strängvektor till en instans av någon av dina underklasser till book-klassen (roman osv.) baserat på vilken boktyp du har i vektorn. Så om du har boktyp "roman" i vektorn så ska du skapa en instans av Roman-klassen med titel, skribent. (detta steg hade jag nog gjort en ny metod för)
5. spara varje instans av book-underklasserna i en (ny) lista av typen book.

Permalänk
Medlem

@elBenko:

tack! vad menas med "1", "2", "3", är det där det är menat att jag ska skriva "title", "author", "typ" och då hamnar strängarna så i min lista? förstår inte riktigt... förstår inte hur jag ska kunna använda vektorn/listan till att skriva ut mina böcker en och en baserat på min textfil :/

Permalänk
Medlem

@elBenko:

string[] myVector = new string[] { "a", "b", "c" }; // skapar strängvektorn
List<string[]> myList = new List<string[]>(); // skapar en lista av typen strängvektor
myList.Add(myVector); // adderar vektorn till listan

för att använda min lista och vektor för att skriva ut mina böcker korrekt, kan jag göra en ny bok såhär genom att koppla den till min bok-klass (new book) och sen ta mina tre egenskaper från bok-klassen såhär:
myList.Add(new book(myVector[0], myVector[1], myVector[2]));

och vart lägger jag nu in b.Split(new string[] { "###" }, StringSplitOptions.None)? :S

Permalänk
Medlem

@Missyym: Japp så kan du t.ex. göra för att omvandla strängvektorn till en bokinstans och lagra direkt i boklistan, men tänk på att du ska använda dina underklasser baserat på vilken boktyp du har i strängvektorn. Så du får använda dig av if-satser eller ett switch-block.

Vad jag menade med 1.2.3 osv var bara att lägga upp delmomenten i punktform. Blir tydligare om man delar upp funktionaliteten som behövs i delmoment.

Permalänk
Medlem

@elBenko: hej! nu har jag nästan byggt klart all kodi windows forms. Men mitt sista problem är att jag ska slumpa fram olika böcker ur min lista som skrivs ut i textrutan varje gång man klickar på knappen men den slumpar bara ut 0 just nu... förstår inte vad som är fel. hela koden:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; namespace WindowsFormsAppslumpen { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { PeadFile(); } private void button2_Click(object sender, EventArgs e) { Application.Exit(); } private void textBox1_TextChanged(object sender, EventArgs e) { } void PeadFile() { var bookSaver = new List<string>(); //skapar ny lista där mina böcker ska sparas //- om textfilen finns eller inte //läser in min textfil StreamReader reader = new StreamReader(@C:\Users\avi\Downloads\exam_1512381521.txt, Encoding.Default, true); string book = ""; while ((book = reader.ReadLine()) != null) { bookSaver.Add(book); } foreach (string b in bookSaver) //för varje sträng i listan: { string[] myVector = b.Split(new string[] { "###" }, StringSplitOptions.None); List<string[]> myList = new List<string[]>(); // skapar en lista av typen strängvektor myList.Add(myVector); // adderar vektorn till listan List<Book> finaList = new List<Book>(); { switch (myVector[2]) { case "Roman": finaList.Add(new Roman(myVector[0], myVector[1], myVector[2])); break; case "Novellsamling": finaList.Add(new Tidskrift(myVector[0], myVector[1], myVector[2])); break; case "Tidskrift": finaList.Add(new Novellsamling(myVector[0], myVector[1], myVector[2])); break; } } foreach (Book c in finaList) { Random slumpGenerator = new Random(); textBox1.Text = slumpGenerator.Next(finaList.Count).ToString(); } } } } } public class Book { public static int NumberOfBooks; public string title; public string author; public string typ; public Book(string title, string author, string typ) { this.title = title; this.author = author; this.typ = typ; NumberOfBooks++; } public string GetTitle() { return title; } public string GetAuthor() { return author; } public string GetTyp() { return typ; } public void SetTitle(string title) { this.title = title; } public override string ToString() { return title + " " + author + " " + typ; } } public class Tidskrift : Book { public Tidskrift(string title, string author, string typ) : base(title, author, typ) { typ = "Tidskrift"; } public override string ToString() { return title + " av " + author + "." + "(" + typ + ")"; } } public class Roman : Book { public Roman(string title, string author, string typ) : base(title, author, typ) { typ = "Roman"; } public override string ToString() { return title + " av " + author + "." + "(" + typ + ")"; } } public class Novellsamling : Book { public Novellsamling(string title, string author, string typ) : base(title, author, typ) { typ = "Novellsamling"; } public override string ToString() { return title + " av " + author + "." + "(" + typ + ")"; } }

Permalänk
Medlem

foreach (Book c in finaList) { Random slumpGenerator = new Random(); textBox1.Text = slumpGenerator.Next(finaList.Count).ToString(); }

Tror inte du ska ha en foreach-loop här, sen vill du väl ha ut informationen om boken på platsen som slumpas fram, inte själva heltalet.

Så typ så här:

Random slumpGenerator = new Random(); textBox1.Text = finaList[slumpGenerator.Next(finaList.Count)].ToString();