Laden...

Bestimmte Zeit lang auf eine Eingabe warten

Letzter Beitrag vor 4 Monaten 58 Posts 1.306 Views
Thema geschlossen
Bestimmte Zeit lang auf eine Eingabe warten

Hi,

ich bin ganz neu hier.
Zur Info: Ich bin Azubi im ersten Lehrjahr und daher bin ich ein ziemlicher Neuling im Programmieren.

Ich möchte in meiner Konsolenanwendung, ähnlich wie bei einem Windows-boot, für eine bestimmte Zeit, einen bestimmten Key drücken können, um zu einem bestimmten Abschnitt des Codes zu gelangen, ähnlich wie, als würde ich das BIOS unter Windows aufrufen wollen. Falls in dieser Zeit die gewünschte Eingabe dann nicht erfolgt, soll ganz normal mit dem Code fortgefahren werden. Hat jemand Ahnung davon? Gerne auch mit Codebeispiel.

LG 
Elmini08

Hallo und willkommen,

dafür kannst du Console.KeyAvailable sowie Console.ReadKey verwenden. Schau dir auch die Beispiele dort an.

Du mußt dann nur eine Schleife verwenden, welche basierend auf der Zeit (mittels DateTime.Now) die vergangenen Sekunden abwartet, bevor der restliche Code ausgeführt wird (alternativ kannst du auch die Klasse Stopwatch benutzen).

Neuling im Programmieren.

Generell geht es beim Programmieren um die Frage wie sich der Compiler (Interpreter / Transpiler / ...) verhält, wenn er auf ein bestimmtes Schlüsselwort (evtl. i.V. mit anderen Schlüsselwörtern) (nicht) trifft.   Der C#-Compiler gibt auch Leitplanken an formell korrekten Code vor (neuere Compiler-Versionen sind hier aber liberaler). Und aus menschlicher Sicht ist der Code so zu schreiben, dass häufige(re) Änderungen oder Erweiterungen nur an einer zentralen Stelle vorzunehmen sind.

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

Hi nochmal,

kann jemand evtl. mal zu dem DateTime.Now ein Codebeispiel zur Verfügung Stellen? Ich verstehe nicht ganz wie ich des in eine Schleife packe.

Danke schonmal!

VG
Elmini08

Mein Programm aktuell:
using System;

namespace Getränkeautomat
{
    internal class ProgrammGetränkeautomat
    {
        static void Main()
        {
            //Begrüßung
            Console.WriteLine("Programm is booting");

            //hier soll die Zeit verstreichen in der man den bestimmten key drücken kann um in die AdminOberfläche zu kommen 
            string sAdminInterface = Convert.ToString(Console.ReadKey());

            if (sAdminInterface == "I")
            {
                goto CommandLineInterface;
            }
            
//ganz viel anderer Code
            
            CommandLineInterface:
            {
                Console.WriteLine("TEST");
            }
        }   
    }
}

In der Dokumentation recherchieren (oder KI befragen) gehört auch zur SW-Entwicklung...

Hier könnten Befehle wie "DateTime jetzt = DateTime.Now; "   "DateTime zukunft = jetzt.AddSeconds( /*Ganzzahl */ ); "  "while (DateTime.Now < zukunft) ..."  weiterhelfen

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

..und den goto-Käse sein lassen.

Warum gotosein lassen? Begründung wäre schön xD

Siehe mein Beitrag in https://mycsharp.de/forum/posts/3841689 unterer Teil.

Erst 3 Wochen alt.

Zitat von Th69

Lies mal über Spaghetticode.

Findest du das ist "Spaghetticode"?

					//User wählt Biersorte 1
					case 1:
						{
							if (iAnzahlVerfügbarkeitNummer1 <= 0)
							{
								Console.WriteLine("Diese Biersorte ist aktuell nicht verfügbar!");
                                Console.WriteLine();
                                break;
							}

							if (iAnzahlVerfügbarkeitNummer1 <= 5)
							{
                                Console.WriteLine("Es sind nur noch " + iAnzahlVerfügbarkeitNummer1 + " Flaschen dieser Biersorte verfügbar!");
                                Console.WriteLine();
                            }

							Console.WriteLine(sBiersorteNummer1 + "wurde deiner Aktuellen Auswahl hinzugefügt");
							Console.WriteLine("Preis pro Bier: {0:f} EUR", dPreisNummer1);
							Console.WriteLine();

							iAnzahlVerfügbarkeitNummer1--;
							
							dEndpreis += dPreisNummer1;

							Console.WriteLine("Preis deiner aktuellen Auswahl {0:f} EUR", dEndpreis);
							Console.WriteLine();
							Console.WriteLine("Möchtest du ein weiters Bier deiner Auswahl hinzufügen? Dann drücke die jeweilige Nummer der gewünschten Biersorte.");
							Console.WriteLine("Möchtest du kein weiters Bier drücke Taste <0>");
							Console.WriteLine();

							//Array für Ausgabe der Biere
							//ArrayZähler für nächste Belegung von Bit von Array
							sGewählteBiere[iArrayZähler] = sBiersorteNummer1 ;
							iArrayZähler += 1;

							//Maximal-Bestellwert    
							if (iArrayZähler == sGewählteBiere.Length)
							{
								Console.WriteLine("Maximal-Bestellwert erreictht. Bitte bezahlen sie nun.");
								Console.WriteLine();
								bWeitersBier = false;
							}
							break;
						}

So startet Spaghetti-Code. Daher: ja.

Wie gesagt; goto hat in strukturierten Sprachen keinen direkten Anwendungsfall mehr. Verwende eine Sprache so, wie sie gedacht ist.

Zitat von Elmini08

Findest du das ist "Spaghetticode"?

Dort verwendest du zwar kein goto, aber verbesserungswürdig ist der Code allemal (insb. wenn deine anderen case-Blöcke ähnlich aufgebaut sind).

Ich weiß, daß für einen Anfänger ersteinmal wichtig ist, daß der Code überhaupt funktioniert. Wenn man jedoch professionell Software entwickelt, dann sollte man sich immer selbst hinterfragen, ob der Code lesbar und wartbar ist.

Hier ein paar generelle Tipps:

  • Verwendung von Container-Datentypen (wie z.B. Array (T[]), List<T>, Directory<K, V>, s.a. [Übersicht] Collections (Auflistungen) ab .NET Framework 2.0) anstatt Einzelvariablen (wie bei dir sBiersorteNummer1- wie oben schon geschrieben, nehme ich an, du hast für die anderen case-Blöcke dann ähnlichen Code, nur eben bei den Variablen mit anderer Zahl am Ende?!)
  • längere Codezeilen sollten in einzelne (sinnvoll benannte) Methoden ausgelagert werden (ca. 10-20 Zeilen)
  • keine Vermischung von UI (Eingabe/Ausgabe) mit Logik (Berechnungen), d.h. Auslagerung in verschiedene Klassen (und Methoden)

Bei größeren Projekten sind dann weitergehende Themen zu beachten, wie z.B. in den Forenthemen FAQ bzw. Artikel beschrieben, insb. [Artikel] Drei-Schichten-Architektur.

"Spaghetticode"?

Es gibt noch mehr Möglichkeiten aus menschlicher Sicht schwer(er) lesbaren oder erweiterbaren Code zu schreiben, weitere 25 (Codier-)Empfehlungen werden hier aufgeführt (gilt für Java, unter C# sieht die Sache aber ähnlich aus) : https://www.javaguides.net/2024/05/25-best-practices-for-java-developers.html

Im Grund gilt es die Teile der Software mit geringem Änderungsgrad (möglichst geschickt) zusammenzufügen und die Teile mit (sehr) hohem Änderungsgrad (möglichst geschickt) auszulagern.  Aber ab einer gewissen Komplexität dürften selbst Spezialisten-Teams ein Problem mit der implementierten Software haben.

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

Zitat von Th69

Dort verwendest du zwar kein goto, aber verbesserungswürdig ist der Code allemal (insb. wenn deine anderen case-Blöcke ähnlich aufgebaut sind).

  • Verwendung von Container-Datentypen (wie z.B. Array (T[]), List<T>, Directory<K, V>, s.a. [Übersicht] Collections (Auflistungen) ab .NET Framework 2.0) anstatt Einzelvariablen (wie bei dir sBiersorteNummer1- wie oben schon geschrieben, nehme ich an, du hast für die anderen case-Blöcke dann ähnlichen Code, nur eben bei den Variablen mit anderer Zahl am Ende?!)

Das ist richtig. Jeder case-Block ist ziemlich das selbe und Tatsächlich ändern sich nur die zahlen der einzelnen Variablen. Ich habe schon versucht mit Directory zu arbeiten, doch wie bekomme ich es hin, dass sich die Variablen in jedem Case ändern?

Bitte um Hilfe!

Danke!
Elmini08

Ähnlich wie du es schon so in deinem Code benutzt:

sGewählteBiere[iArrayZähler] = sBiersorteNummer1 ;

Wenn du also anstatt iAnzahlVerfügbarkeitNummer1--;(welches nur eine Vereinfachung von iAnzahlVerfügbarkeitNummer1 = iAnzahlVerfügbarkeitNummer1 - 1darstellt), hierfür eine List<int>benutzt:

iAnzahlVerfügbarkeitNummern[index]--;

wobei index dann die Variable ist, welche du für den switch über die Biersorte bisher benutzt hast (beachte außerdem, daß der index 0-basiert ist, während du bisher bei 1 beginnst, d.h. index = biersorte - 1).

PS: Die sog. Ungarische Notation  (genauer "Systems Hungarian"), d.h. die Benennung der Variablen anhand des Typen (i, d, s, ...), verwendet man in modernem Code nicht mehr.

Für .NET gibt es die Frameworkentwurfsrichtlinien.

Danke für die Hilfe schonmal.

Des mit der ungarischen Notation hat uns unser Lehrer in der Berufsschule beigebracht. Kannte es nicht anders.

Habe Tatsächlich auch bei 0 begonnen. Case 0 ist zum "Abbrechen der Bestellung" Sieht man auch im Code. Dass man <0> drücken soll um keine weiteren Biere der Auswahl hinzuzufügen.

Aber Danke dir! Werde des mit derListgleich mal ausprobieren.

LG
Elmini08

Berufsschule

(etwa offtopic) jetzt könnte die Gefahr bestehen, dass weitere Codier-Methoden gelehrt werden, die früher durchaus Sinn hatten.  Und das Aufgaben mit Mitteln von früher bearbeitet werden wollen  (und dann auf modern(er)e Ratschläge trifft)

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

Hi nochmal,

ich habe versucht mit der Listzu arbeiten wie mir empfohlen wurde:

                    //User wählt Biersorte 1
                    case 1:
                        {
                            iBiersortenwahl--;

                            if (iAnzahlVerfügbarkeitNummer1 <= 0)
                            {
                                Console.WriteLine(lAusgaben[1]);
                                break;
                            }

                            if (iAnzahlVerfügbarkeitNummer1 <= 5)
                            {
                                Console.WriteLine(lAusgaben[6] + lAnzahlverfügbarkeitnummern[iBiersortenwahl] + lAusgaben[7]);
                                Console.WriteLine();
                            }

                            //Berechnungen 
                            lAnzahlverfügbarkeitnummern[iBiersortenwahl]--;
                            dEndpreis += dPreisNummer1;

                            //Array für Ausgabe der Biere
                            //ArrayZähler für nächste Belegung von Bit von Array
                            sGewählteBiere[iArrayZähler] = lBiersorten[iBiersortenwahl];
                            iArrayZähler += 1;

                            //Ausgabe was bestellt wurde
                            Console.WriteLine(sBiersorteNummer1 + lAusgaben[3] + "{0:f}" + lAusgaben[4] + "{1:f}" + lAusgaben[5], dPreisNummer1, dEndpreis);

                            //Maximal-Bestellwert    
                            if (iArrayZähler == sGewählteBiere.Length)
                            {
                                Console.WriteLine(lAusgaben[2]);
                                bWeitersBier = false;

                            }

                            //Nachfrage ob mehr Bier
                            Console.WriteLine(lAusgaben[0]);
                            break;

Doch genau so wie @Th69 mir empfohlen hatte (lAnzahlVerfügbarkeitNummern[iBiersortenwahl]--), bekomme ich eine Exeption:

Unbehandelte Ausnahme: System.ArgumentOutOfRangeException: Der Index lag außerhalb des Bereichs. Er darf nicht negativ und kleiner als die Sammlung sein.
Parametername: index
bei System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
bei System.Collections.Generic.List`1.get_Item(Int32 index)
bei Getränkeautomat.ProgrammGetränkeautomat.Main() in C:\Users\Elmini08\source\repos\Getränkeautomat\Program.cs:Zeile 241.

Kann jemand helfen?

VG
Elmini08

Hast du die List<int> denn auch mit Werten gefüllt (List<T>.Add)?

Außerdem habe ich die Liste vorgeschlagen, so daß du kein switch-case mehr für die einzelnen Biersorten benötigst, sondern du nur noch einen Block (bzw. am besten als eigene Methode ausgelagert) benötigst, d.h. die ganzen Einzelvariablen (also die mit der Ziffer am Ende) entfernst, wie z.B.

// statt:  if (iAnzahlVerfügbarkeitNummer1 <= 0)
if (lAnzahlverfügbarkeitnummern[iBiersortenwahl] <= 0)

Ebenso müßtest du für die dPreisNummerX auch eine List<double>anlegen.

Bzw. Wenn du lerntechnisch schon soweit bist, sogar am besten eine eigene Klasse Biersorte(mit den Eigenschaften für Anzahl, Preis, etc.) erzeugen und dann eine List<Biersorte>erstellen, die du dann benutzt.

Habe 3 Listen erstellt:


//LISTEN
//LISTEN
//LISTEN

//Liste für Biersorten während Cases
List<string> lBiersorten = new List<string>();
{
	//index 0
	sBiersorteNummer1 = "Helles Bier ";

	//index 1
	sBiersorteNummer2 = "Pils ";

	//index 2
	sBiersorteNummer3 = "Schwarzbier ";

	//index 3
	sBiersorteNummer4 = "Weizen ";

	//index 4
	sBiersorteNummer5 = "Lagerbier ";

	//index 5
	sBiersorteNummer6 = "Kellerbier ";

	//index 6
	sBiersorteNummer7 = "Bockbier ";

	//index 7
	sBiersorteNummer8 = "Zwickl ";
}

//Liste für Abfragen während Cases
List<string> lAusgaben = new List<string>
{
	//index 0
	"Möchtest du ein weiters Bier deiner Auswahl hinzufügen? Dann drücke die jeweilige Nummer der gewünschten Biersorte.\nMöchtest du kein weiters Bier drücke Taste <0>.\n",

	//index 1
	"Diese Biersorte ist aktuell nicht verfügbar!\n",

	//index 2
	"Maximal-Bestellwert erreicht. Bitte bezahle jetzt.\n",

	//index 3
	"wurde deiner Aktuellen Auswahl hinzugefügt. \nPreis pro Bier: " ,
	
	//index 4			
	"EUR. \n \n\"Preis deiner aktuellen Auswahl: ",

	//index 5
	"EUR",

	//index 6
	"Es sind nur noch ",

	//index 7
	" Flaschen dieser Biersorte verfügbar\n"
};

//Liste für Variablen während Cases
List<int> lAnzahlverfügbarkeitnummern = new List<int>();
{
	//index 0
	iAnzahlVerfügbarkeitNummer1 = 8;

	//index 1
	iAnzahlVerfügbarkeitNummer2 = 0;

	//index 2
	iAnzahlVerfügbarkeitNummer3 = 0;

	//index 3
	iAnzahlVerfügbarkeitNummer4 = 0;

	//index 4
	iAnzahlVerfügbarkeitNummer5 = 0;

	//index 5
	iAnzahlVerfügbarkeitNummer6 = 0;

	//index 6
	iAnzahlVerfügbarkeitNummer7 = 0;

	//index 7
	iAnzahlVerfügbarkeitNummer8 = 0;
}

Habe auch versucht die VerfügbarkeitsNummern mit Index zu verwenden. Dann bekomme ich aber genau an dieser Stelle meine Exeption. 
Was kann ich machen?

Danke!

Elmini08

Du solltest dich mal mit der Syntax von C# genauer beschäftigen.

Bei deinem Code hast du bei der 1. und 3. Liste diese nicht mit Werten gefüllt, sondern, wegen dem Semikolon nach der Listenerzeugung, danach einen Block mit weiteren Zuweisungen an die einzelnen Variablen.

Richtig muß es also so aussehen:

List<string> lBiersorten = new List<string>() // <-- hier KEIN Semikolon am Ende
{
    //index 0
    "Helles Bier ", // <-- Listenwerte werden mit Komma voneinander getrennt

    //index 1
    "Pils ",
    
    // ...
}; // <-- hier nun ein Semikolon als Abschluß der gesamten Anweisung (engl. statement)

Bei deiner 2. Liste paßt aber die Syntax (dies hast du wohl irgendwo her kopiert).

Und nun lösche noch alle Einzelvariablen (sBiersorteNummer1, sBiersorteNummer2, ... sowie iAnzahlVerfügbarkeitNummer1, iAnzahlVerfügbarkeitNummer2, ...), damit du diese nicht noch weiter verwendest.

PS: Nach welchem Buch lernst du denn bzw. hast du denn keinen Tutor (Ausbilder)?

Oh man ja ist ja nur logisch das man keine Variablen in eine Listpacken kann xD. 🤦‍♂️🤦‍♂️

Lerne nach keinem Buch. Mein Ausbilder ist selten verfügbar (Ist Abteilungsleiter und viel beschäftigt und zudem kann er mittlerweile weniger C# als ich). Ich eigne mir über googlen, Foren und Youtoube Videos immer wieder neue Sachen an. Die Sachen die wir in der Schule machen sind "Pille-Palle". Sind da grade bei Arrays (erstes Lehrjahr eben).

Danke Dir! Echt!

LG 
Elmini08

Dann nimm Dir wenigstens 4h Zeit, und mach die Grundlagen in der .NET Doc durch. Weil aktuell wirkt es so, dass Du eher nach dem Stocher-Prinzip vorgehst - das wird nix. Da brauchste 100x so lang für nen Fortschritt - und die Leute um Dich herum ziehen an Dir mit Wissen vorbei.

Wie kann ich hier mit einer Liste arbeiten? Ich habe einen Array angelegt in dem alle gewählten Biere abgelegt werden. Falls man mehrmals die gleiche Biersorte wählt soll zusammengezählt werden. Habe jetzt aber für jede Biersorte eine eigene Schleife hinzugefügt.

                //Ausgabe der Biersorten
                {
                    {
                        //Wie oft Biersorte 1
                        for (int iBitdesArraysSorte1 = 0; iBitdesArraysSorte1 < sGewählteBiere.Length; iBitdesArraysSorte1++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte1] == sBiersorteNummer1)
                                iAnzahlBiersorte1++;
                        }
                        //Wie oft Biersorte 2
                        for (int iBitdesArraysSorte2 = 0; iBitdesArraysSorte2 < sGewählteBiere.Length; iBitdesArraysSorte2++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte2] == sBiersorteNummer2)
                                iAnzahlBiersorte2++;
                        }
                        //Wie oft Biersorte 3
                        for (int iBitdesArraysSorte3 = 0; iBitdesArraysSorte3 < sGewählteBiere.Length; iBitdesArraysSorte3++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte3] == sBiersorteNummer3)
                                iAnzahlBiersorte3++;
                        }
                        //Wie oft Biersorte 4
                        for (int iBitdesArraysSorte4 = 0; iBitdesArraysSorte4 < sGewählteBiere.Length; iBitdesArraysSorte4++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte4] == sBiersorteNummer4)
                                iAnzahlBiersorte4++;
                        }
                        //Wie oft Biersorte 5
                        for (int iBitdesArraysSorte5 = 0; iBitdesArraysSorte5 < sGewählteBiere.Length; iBitdesArraysSorte5++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte5] == sBiersorteNummer5)
                                iAnzahlBiersorte5++;
                        }
                        //Wie oft Biersorte 6
                        for (int iBitdesArraysSorte6 = 0; iBitdesArraysSorte6 < sGewählteBiere.Length; iBitdesArraysSorte6++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte6] == sBiersorteNummer6)
                                iAnzahlBiersorte6++;
                        }
                        //Wie oft Biersorte 7
                        for (int iBitdesArraysSorte7 = 0; iBitdesArraysSorte7 < sGewählteBiere.Length; iBitdesArraysSorte7++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte7] == sBiersorteNummer7)
                                iAnzahlBiersorte7++;
                        }
                        //Wie oft Biersorte 8
                        for (int iBitdesArraysSorte8 = 0; iBitdesArraysSorte8 < sGewählteBiere.Length; iBitdesArraysSorte8++)
                        {
                            if (sGewählteBiere[iBitdesArraysSorte8] == sBiersorteNummer8)
                                iAnzahlBiersorte8++;
                        }
                    }

LG
Elmini08

Wie würdest du das analog lösen? Also wenn du keinen Computer hast und du musst die Bestellung aufnehmen?

Wenn du dir das klar gemacht hast, dann suchst du nach einer Struktur, in der man das im Computer (Programmiersprache) abbilden kann. Und das hier ist ein klassisches Problem wofür man auch schon eine fertige Struktur erwarten kann (die es auch gibt).

Hat die Blume einen Knick, war der Schmetterling zu dick.

Dies gehört zum Lernen einer Programmiersprache dazu, daß man überlegen muß, wie man den Code besser schreiben kann.

Mir scheint (anhand deiner bisherigen Codeausschnitte), daß du bisher alles in einem großen Codeblock geschrieben hast. Du solltest dich dringend mit Methoden beschäftigen.

Wenn du bei deinem Code die Schleife in eine eigene Methode (mit passendem Rückgabewert und Parametern) auslagerst, dann erkennst du (hoffentlich) selber, wie du daraus dann den Code auf eine Liste übertragen kannst.

PS: Auch deine Namensgebung ist eigenartig: statt BitdesArrays solltest du index verwenden.

Und wenn du schon kein Buch verwendest, dann solltest du die C#-Sprachdokumentation durcharbeiten (bzw. zum Nachlesen) verwenden.

Mit einem Dictionary geht das erheblich einfacher.

    internal class Program
    {
        private static readonly Random random = new();
        private static Dictionary<int, int>? bierDictionary;

        static void Main(string[] args)
        {
            CreateDictionary(8);
            List<int> gewaehlteBiere = ChooseBeers();
            AssignToDictionary(gewaehlteBiere);
            Print(gewaehlteBiere);
            Console.ReadKey();
        }

        private static void Print(List<int> gewaehlteBiere)
        {
            Console.WriteLine("Gewählte Biere: [ {0} ]", string.Join(", ", gewaehlteBiere));
            foreach (KeyValuePair<int, int> sorte in bierDictionary)
            {
                Console.WriteLine($"{sorte.Key} --> {sorte.Value}x");
            }
        }

        private static void AssignToDictionary(List<int> gewaehlteBiere)
        {
            foreach (int sorte in gewaehlteBiere)
            {
                bierDictionary[sorte]++;
            }
        }

        private static List<int> ChooseBeers()
        {
            List<int> gewaehlteListe = [];
            for (int i = 0; i < 16; i++)
            {
                gewaehlteListe.Add(random.Next(1, 9));
            }
            return gewaehlteListe;
        }

        private static void CreateDictionary(int anzahlBiersorten)
        {
            bierDictionary = [];
            for (int i = 1; i <= anzahlBiersorten; i++)
            {
                bierDictionary.Add(i, 0);
            }
        }
    }

@Caveman
Eigentlich kann man auf das Dictionary komplett verzichten und direkt mit einem Array arbeiten.
Du musst nur über den Index direkt auf das Array zugreifen und dort dann die Einträge hochzählen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

etz check ich gar nix mehr😅😂

Deshalb solltest du den Rat von Abt folgen und dir mal die Doku anschauen und mit entsprechenden Lehrmaterial dich einarbeiten.
Anders kommst du nicht an das Wissen und die Erfahrung um den Code zu verstehen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Zitat von T-Virus

@Caveman
Eigentlich kann man auf das Dictionary komplett verzichten und direkt mit einem Array arbeiten.
Du musst nur über den Index direkt auf das Array zugreifen und dort dann die Einträge hochzählen.

Ja, das stimmt! Ich habe da nicht nachgedacht und aus Gewohnheit das Dictionary verwendet. Mit einem Dictionary kann man auch als Schlüssel einen String verwenden und muss sich dann nicht immer Zahlen merken - Welche Biersorte hat nochmal den Index 4?!

Wie kann ich hier mit einer Liste arbeiten? Ich habe einen Array angelegt in dem alle gewählten Biere abgelegt werden. Falls man mehrmals die gleiche Biersorte wählt soll zusammengezählt werden

Hab die Lösung gefunden:

                int iIndex;
                int iIndexBiersorten = 0;
                int iWelcheBiersorte = 0;
                
                do
                {
                    for (iIndex = 0; iIndex < sGewählteBiere.Length; iIndex++)
                    {
                        if (sGewählteBiere[iIndex] == lBiersorten[iWelcheBiersorte])
                        {
                            lAnzahlBiersorten[iIndexBiersorten]++;
                        }
                    }					
                    iIndexBiersorten++;
                    iWelcheBiersorte++;
                }
                while (iIndexBiersorten == iVerfügbareBiersorten);	
                
                for(iIndex = 0; iIndex < iVerfügbareBiersorten;  iIndex++)
                {
                    if (lAnzahlBiersorten[iIndex] > 0)
                    {
                        Console.WriteLine(lAnzahlBiersorten[iIndex] + "x " + lBiersorten[iIndex]);
                    }
                }

Danke für eure Hilfe!

LG

Elmini08

Da stimmt die Logik in der do...while-Schleife aber noch nicht. Der Ausdruck in while(...)ist kein Abbruchkriterium, sondern, wie der mittlere Ausdruck bei einer for-Schleife, die Bedingung, solange die Schleife ausgeführt werden soll.

Benutze am besten den Debugger, um den Ablauf des Codes zu verstehen: [Artikel] Debugger: Wie verwende ich den von Visual Studio?

Zitat von Th69

Da stimmt die Logik in der do...while-Schleife aber noch nicht. Der Ausdruck in while(...)ist kein Abbruchkriterium, sondern, wie der mittlere Ausdruck bei einer for-Schleife, die Bedingung, solange die Schleife ausgeführt werden soll.

Benutze am besten den Debugger, um den Ablauf des Codes zu verstehen: [Artikel] Debugger: Wie verwende ich den von Visual Studio?

Fehler ist mir schon aufgefallen. Habe umgeändert in !=.

Findet Ihr das ist immernoch Spaghetticode?:

//Do/while Schleife falls falsche Nummer gewählt wird
do
{
    //Man kann nicht mehr einfach Enter drücken oder einen Buchstaben eingeben. Exeptions werden abgefangen.
    do
    {
        Console.WriteLine("Wähle deine Biersorte:");
        Console.WriteLine();
        
        //gibt keine Zahl ein (z.B Buchstabe, oder enter etc...
        if (!Int32.TryParse(Console.ReadLine(), out iBiersortenwahl))
        {                           
            Console.WriteLine();
            Console.WriteLine("Der eingegebene Wert muss eine Zahl sein!");
            Console.WriteLine();
            bKeineZahleingegeben = true;
        }
        //gibt negative Zahl ein
        else if (iBiersortenwahl < 0)
        {
           Console.WriteLine();
           Console.WriteLine("Der eingegebene Wert muss positiv sein!");
            Console.WriteLine();
            bKeineZahleingegeben = true;
        }
        //gibt richtig ein
        else
        {
            bKeineZahleingegeben = false;
        }
    }
    while (bKeineZahleingegeben);
    Console.WriteLine();
    {
        if (iBiersortenwahl == 0)
        {
            Console.WriteLine("Es werden nun keine weiteren Biere deiner Auswahl hinzugefügt.");
            bWeitersBier = false;
        }
        else if (iBiersortenwahl != 0 | iBiersortenwahl > iVerfügbareBiersorten)
        {
            iBiersortenwahl--;
            if (lAnzahlverfügbarkeitnummern[iBiersortenwahl] <= 0)
            {
                Console.WriteLine(lAusgaben[1]);
                continue;
            }
            else if (lAnzahlverfügbarkeitnummern[iBiersortenwahl] <= 5)
            {
                Console.WriteLine(lAusgaben[6] + lAnzahlverfügbarkeitnummern[iBiersortenwahl] + lAusgaben[7]);
                Console.WriteLine();
           }
            //Berechnungen 
            lAnzahlverfügbarkeitnummern[iBiersortenwahl]--;
            dEndpreis += lPreise[iBiersortenwahl];
            
            //Array für Ausgabe der Biere
            //ArrayZähler für nächste Belegung von Bit von Array
            sGewählteBiere[iArrayZähler] = lBiersorten[iBiersortenwahl];
            iArrayZähler += 1;
            
            //Ausgabe was bestellt wurde
            Console.WriteLine(lBiersorten[iBiersortenwahl] + lAusgaben[3] + "{0:f}" + lAusgaben[4] + "{1:f}" + lAusgaben[5], lPreise[iBiersortenwahl], dEndpreis);
            
            //Maximal-Bestellwert    
            if (iArrayZähler == sGewählteBiere.Length)
            {
                Console.WriteLine(lAusgaben[2]);
                bWeitersBier = false;
            }
            
            //Nachfrage ob mehr Bier
            Console.WriteLine(lAusgaben[0]);
        }
        
        //User drückt den Falschen Key
        else
        {
            Console.WriteLine("Unter diesem Wert ist kein Bier hinterlegt!");
            Console.WriteLine();
            Console.WriteLine(lAusgaben[0]);
        }                    
    }
}
//While Fußsteuerung
while (bWeitersBier);
}

Die Antwort lautet vermutlich ja! Warum?

Weil wie die Spaghettis der Code in dieser Methode viel zu lang ist.

Die Methode beinhaltet zu viele Aufgaben.

Besonders der zweite Teil - nach der inneren do while Schleife - schwer verständlich ist.

Wie würdest du den Code verständlicher machen?

Beispielsweise kann man die innere do while Schleife komplett in eine eigene Methode auslagern. In dieser ausgelagerten Methode kannst Du dann auch noch den falschen Key mit integrieren!

Es gibt nicht die eine Definition von Spaghetti-Code, aber es sind die Aspekte, die einen Urteilen lassen, ob etwas Spaghetti-Code ist - oder nicht

  • Übersichtlichkeit → Nein, Dein Code ist wirklich nicht übersichtlich
  • Strukturierung → Nein, Dein Code ist nicht strukturiert
  • Trennung von Verantwortlichkeiten → Dein Code vermischt alles

.. und dann noch ein paar Aspekte mehr wie Modularisierung, Kopplung, Wartbarkeit.

Das sind aber Aspekte, die bei Deinen 50 Zeilen völlig egal sind.

Was Du machen kannst:

  • Ja, mach wirklich das, was die anderen sagen, und verwende Dinge wie Methoden, um Code auszulagern
  • Arbeite mit Variablen statt UI-Operationen direkt in Methoden zu kippen - fördert die Lesbarkeit
  • Trenne Deine Logik von der UI ([Artikel] Drei-Schichten-Architektur)
  • Lass diese Prefixes weg - wir sind in 2024 in C# und nicht 1980 in Perl
  • Halte Dich an die Sprach- und Schreibguidelines der jeweiligen Programmiersprache, hier eben C# Coding Guidelines.

Lohnt sich das bei Deinen 50 Zeilen? Musst selbst wissen.

Dein Code ist insgesamt eher schlecht lesbar, weil man dauernd nachschauen muss, was was ist. Wenn jemand Dein Code nicht auswendig kennt, kennt er nicht die Bedeutung von lAusgaben[2] - man muss rum scrollen etc. Genau dafür gibts Klassen.


Meine persönlichen 50 Cent: seh ich deutschen Code, ist das automatisch für mich schlechter lesbar und ich bekomm Würgen.

Zitat von Abt

Meine persönlichen 50 Cent: seh ich deutschen Code, ist das automatisch für mich schlechter lesbar und ich bekomm Würgen.

Wie meinst Du das?

a) Deutsche programmieren schei..e?

b) Verwendung von deutschen Variablen- und Methodennamen? Da würde ich Dir zustimmen. Das macht man nicht.

Oh well.. hab natürlich die "Verwendung der deutschen Sprache im Code" gemeint 😉 Auf die Idee, dass Deutsche scheiss Code schreiben, kam ich beim Durchlesen nicht 😃

Lohnt sich das bei Deinen 50 Zeilen? Musst selbst wissen.

Dein Code ist insgesamt eher schlecht lesbar, weil man dauernd nachschauen muss, was was ist. Wenn jemand Dein Code nicht auswendig kennt, kennt er nicht die Bedeutung von lAusgaben[2] - man muss rum scrollen etc. Genau dafür gibts Klassen.


Meine persönlichen 50 Cent: seh ich deutschen Code, ist das automatisch für mich schlechter lesbar und ich bekomm Würgen.

  1. Der Code ist ja nicht nur 50 Zeilen Lang sondern um genau zu sein 461.
  2. Finde es selber sehr Umständlich zu erstellen aber bekomme das mit eurer Hilfe schon hin haha
  3. Habe  wirklich keine Ahnung wie man mit Klassen arbeitet. Dieses Programm ist halt mein erstes großes Programm. Bin wie gesagt erst im ersten Ausbildungsjahr.

Bin aber weiterhin für jede Hilfe dankbar.

Elmini08

Mir schon klar, dass Du nicht genau 50 Zeilen hast. 461 ist jetzt auch keine Raketentechnik.
"Echte Team-Projekte" haben hunderttausende oder Millionen von Zeilen Code - also ein ganz anderer Anspruch an Code. Das Forum hier hat ca. 150.000 Zeilen Code. Das sind aber nur Mengenangaben, sagt nicht wirklich was über Komplexität aus.


Gerade weil Du Anfänger bist - waren wir alle - konzentrier Dich auf das wesentliche:

  • Schreib lesbaren Code
  • Verwende Variablen, Klassen, Methoden und Parameter, sodass Du keinen redundanten, sich wiederholenden Code alá Copy Paste schreibst (ja, muss man lernen, mussten wir alle). Das ist die Grundlage von Sprachen wie C#, Java und Co.
  • Versteh Deinen eigenen Code
  • Halte Dich an die wichtigsten Grundlagen von C#

Servus,

ich habe Dir im Anhang einen Getränkeautomaten mit Klassenstruktur angehängt.
Das Programm ist sicherlich auch nicht perfekt, aber es sollte Dir eine grobe Richtung geben, wie sowas aussehen könnte.
Mit Absicht sind ein paar Methoden nicht umgesetzt (siehe "nicht implementiert" Kommentar).
Die Ausgabefunktionen kann man weiter vereinheitlichen, also Strings erstellen und an die Print-Methode schicken - aber das überlasse ich Dir 😃

ich habe Dir im Anhang einen Getränkeautomaten mit Klassenstruktur angehängt.

Danke dir schonmal.
Leider darf/kann ich auf dem Arbeitslaptop (ich programmiere immer hier in der Arbeit) keine .zip Dateien aus "unbekannten" Quellen herunterladen. Zu dem ändert sich beim abspeichern der .zip der Dateiname komplett. Und die Datei ist heißt nicht mehr so wie unter deinem Beitrag zusehen ist. 
Kannst du den Code normal als Codeblock teilen? Wäre echt super.

Danke nochmal!

Elmini08

Verstehe ich jetzt nicht ganz!

Hast Du keine Möglichkeit, zu Hause die Datei auf einen USB-Stick runterzuladen? Der Name der ZipDatei ist völlig irrelevat und lässt sich auf dem USB-Stick ändern.

Natürlich kann ich hier auch die Codeblöcke reinstellen. Klöppelst Du Dir das dann zusammen?

Verstehe ich jetzt nicht ganz!

Hast Du keine Möglichkeit, zu Hause die Datei auf einen USB-Stick runterzuladen? Der Name der ZipDatei ist völlig irrelevat und lässt sich auf dem USB-Stick ändern.

  1. Keine Datein aus unbekannten quellen --> USB-Stick is eine unbekannte Quelle
  2. Sophos (Firewall Programm) blockt auf den Arbeitsrechnern jegliche USB-Sticks

Schicks einfach hier rein!
Danke

Elmini08

Teil 1 von 2

Okay! Muss das auf zwei Posts verteilen, da mehr als 8000 Zeichen!

Datei Beverage.cs

    public class Beverage
    {
        /// <summary>
        /// Der Getränkename
        /// </summary>
        public string? Name { get; }

        /// <summary>
        /// Der Getränkepreis
        /// </summary>
        public decimal Price { get; }

        /// <summary>
        /// Erstelle eine neue Instanz einer Getränkesorte
        /// </summary>
        /// <param name="name">Der Getränkename</param>
        /// <param name="price">Der Getränkepreis</param>
        public Beverage(string name, decimal price)
        {
            Name = name;
            Price = price;
        }
    }

Datei Slot.cs

    public class Slot
    {
        #region Properties
        /// <summary>
        /// Die Getränkesorte
        /// </summary>
        public Beverage Beverage { get; set; }

        /// <summary>
        /// Die Kapazität des Getränkefachs
        /// </summary>
        public int Capacity { get; set; }

        /// <summary>
        /// Der aktuelle Füllstand des Getränkefachs
        /// </summary>
        public int CurrentLevel { get; private set; }

        /// <summary>
        /// Der Schwellwert des Getränkefachs bei dessen Unterschreitung ein Alarm ausgelöst wird
        /// </summary>
        public int Threshold { get; set; }

        /// <summary>
        /// Die Anzahl der Getränke
        /// </summary>
        public int Selected { get; private set; }
        #endregion

        #region Events
        /// <summary>
        /// Das Ereignis welches ausgelöst wird wenn der Schwellwert unterschritten wird
        /// </summary>
        public event EventHandler? BelowThreshold;

        /// <summary>
        /// Das Ereignis welches ausgelöst wird wenn das Getränkefach leer ist
        /// </summary>
        public event EventHandler? Empty;
        #endregion

        #region Constructors
        /// <summary>
        /// Erstelle eine neue Instanz eines Getränkefaches
        /// </summary>
        /// <param name="beverage">Die Getränkesorte</param>
        /// <param name="capacity">Die Kapazität des Getränkefaches</param>
        /// <param name="threshold">Der Schwellwert</param>
        public Slot(Beverage beverage, int capacity, int threshold)
        {
            Beverage = beverage;
            Capacity = capacity;
            Threshold = threshold;
            CurrentLevel = Capacity;
            Selected = 0;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Die Getränkewahl
        /// </summary>
        public void Select()
        {
            if (CurrentLevel > 0)
            {
                Selected++;
                CurrentLevel--;
                Console.WriteLine($"You selected a {Beverage.Name} for {Beverage.Price}");
                if (CurrentLevel < Threshold)
                {
                    BelowThreshold?.Invoke(this, new EventArgs());
                }
            } 
            else if (CurrentLevel == 0)
            {
                Empty?.Invoke(this, new EventArgs());
            }
        }

        /// <summary>
        /// Setzt die Getränkewahl zurück
        /// </summary>
        public void ResetSelection()
        {
            Selected = 0;
        }

        /// <summary>
        /// Die Getränkeabwahl
        /// Nicht implementiert
        /// </summary>
        public void Unselect()
        {
            Selected--;
            CurrentLevel++;
        }

        /// <summary>
        /// Liefert den Preis von einem Getränk
        /// Nicht implementiert
        /// </summary>
        /// <returns>Der Getränkepreis</returns>
        public string GetPrice()
        {
            return $"Price for {Beverage.Name} is {Beverage.Price:C2}";
        }

        /// <summary>
        /// Liefert den Gesamtpreis von einem Getränk
        /// </summary>
        /// <returns>Der Gesamtpreis von einem Getränk</returns>
        public string GetTotal()
        {
            string message = string.Empty ;
            if (Selected > 0)
            {
                message = string.Format("{0, 2}x {1,-13} a {2,6}€ = {3,6}€", Selected, Beverage.Name, Beverage.Price, Selected * Beverage.Price);
            }
            return message ;
        }
        #endregion
    }
Teil 2 von 2

Datei Vendingmachine.cs

    public class Vendingmachine
    {
        #region Fields
        /// <summary>
        /// Die Getränkefächer
        /// </summary>
        private readonly List<Slot> slots;
        #endregion

        #region Constructors
        /// <summary>
        /// Erstellt eine neue Instanz eines Getränkeautomaten
        /// </summary>
        public Vendingmachine()
        {
            slots = [];
        }
        #endregion

        #region Methods
        /// <summary>
        /// Auffüllen des Getränkeautomaten
        /// </summary>
        public void Fill()
        {
            slots.Add(new Slot(new Beverage("Helles Bier", 2.60m), 20, 5));
            slots.Add(new Slot(new Beverage("Pils", 3.00m), 25, 5));
            slots.Add(new Slot(new Beverage("Schwarzbier", 2.70m), 20, 5));
            slots.Add(new Slot(new Beverage("Weizen", 3.20m), 20, 5));
            slots.Add(new Slot(new Beverage("Lagerbier", 2.80m), 20, 5));
            slots.Add(new Slot(new Beverage("Kellerbier", 2.60m), 20, 5));
            slots.Add(new Slot(new Beverage("Bockbier", 3.40m), 20, 5));
            slots.Add(new Slot(new Beverage("Zwickl", 2.90m), 20, 5));

            RegisterSlots();
        }

        /// <summary>
        /// Gebe den Begrüßungsbildschirm aus
        /// </summary>
        public void Greet()
        {
            Console.WriteLine("###############################");
            Console.WriteLine("#                             #");
            Console.WriteLine("#       Hello Customer!       #");
            Console.WriteLine("#                             #");
            Console.WriteLine("###############################");
            Console.WriteLine("#                             #");
            Console.WriteLine("# {0,3} {1,-13} {2,6} {3,2} #", "Id", "Name", "Price", "No");
            Console.WriteLine("#                             #");
            PrintSlotInfo();
        }

        /// <summary>
        /// Gebe die Getränkefächer am Begrüßungsbildschirm aus
        /// </summary>
        private void PrintSlotInfo()
        {
            for (int i = 0; i < slots.Count; i++)
            {
                Console.WriteLine("# {0,2}. {1,-12} {2,6}€ {3,2} #", i + 1, slots[i].Beverage.Name, slots[i].Beverage.Price, slots[i].CurrentLevel);
            }
        }

        /// <summary>
        /// Die Getränkefachanwahl
        /// </summary>
        /// <param name="slotnumber">Die Getränkefachnummer</param>
        public void Select(int slotnumber)
        {
            if (slotnumber > 0 && slotnumber <= slots.Count)
            {
                int slotIndex = slotnumber - 1;
                slots[slotIndex].Select();
            }
            else if (slotnumber == 0)
            {
                Checkout();
            }
        }

        /// <summary>
        /// Kein weiteres Getränk angewählt
        /// deshalb Ausgabe der gewählen Getränke mit 
        /// </summary>
        private void Checkout()
        {
            decimal total = 0.0m;
            string message = string.Empty;
            Print(string.Empty);
            Print("Checkout:");

            foreach (Slot slot in slots)
            {
                total += slot.Selected * slot.Beverage.Price;
                message = slot.GetTotal();
                if (!string.IsNullOrEmpty(message))
                {
                    Print(message);
                }
                slot.ResetSelection();
            }

            Print(new string('=', 37));
            Print(string.Format("{0,36}€", total));

            Print(string.Empty);
            Print(string.Empty);
            Greet();
        }

        /// <summary>
        /// Konsolenausgabe
        /// </summary>
        /// <param name="message">Die Nachricht die auf der Konsole ausgegeben werden soll</param>
        private void Print(string message)
        {
            Console.WriteLine(message);
        }

        /// <summary>
        /// Abonniere die Getränkefachereignisse
        /// </summary>
        private void RegisterSlots()
        {
            RegisterSlotsThreshold();
            RegisterSlotsEmpty();
        }

        /// <summary>
        /// Aboniere von jedem Getränkefach das Schwellwertereignis
        /// </summary>
        private void RegisterSlotsThreshold()
        {
            foreach (Slot slot in slots)
            {
                slot.BelowThreshold += OnSlotIsBelowThreshold;
            }
        }

        /// <summary>
        /// Aboniere von jedem Getränkefach das Fach leer Ereignis 
        /// </summary>
        private void RegisterSlotsEmpty()
        {
            foreach (Slot slot in slots)
            {
                slot.Empty += OnSlotIsEmpty;
            }
        }

        /// <summary>
        /// Gebe einen Alam aus, wenn das Getränkefach leer ist
        /// </summary>
        /// <param name="sender">Das Getränkefach</param>
        /// <param name="e">Nicht in Verwendung</param>
        private void OnSlotIsEmpty(object? sender, EventArgs e)
        {
            if (sender is Slot slot)
            {
                Console.WriteLine($"ALERT: The beer {slot.Beverage.Name} is empty!");
            }
        }

        /// <summary>
        /// Gebe einen Alam aus, wenn der Schwellwert unterschritten wird
        /// </summary>
        /// <param name="sender">Das Getränkefach</param>
        /// <param name="e">Nicht in Verwendung</param>
        private void OnSlotIsBelowThreshold(object? sender, EventArgs e)
        {
            if (sender is Slot slot)
            {
                Console.WriteLine($"ALERT: The beer {slot.Beverage.Name} is running out!");
            }
        }
        #endregion
    }

Datei Program.cs

    internal class Program
    {
        static void Main()
        {
            Vendingmachine vendingmachine = new();
            vendingmachine.Fill();
            vendingmachine.Greet();
            ConsoleKeyInfo pressedKey;

            do
            {
                pressedKey = Console.ReadKey();
                if (int.TryParse(pressedKey.KeyChar.ToString(), out int slotNumber))
                {
                    vendingmachine.Select(slotNumber);
                }

            } while (pressedKey.Key != ConsoleKey.X);
        }
    }

Aufgabe für Dich: Es werden bei der Konsolenausgabe statt dem € Zeichen nur ? angezeigt. Warum ist das so und wie lässt sich das beheben? Eine zusätzliche Codezeile ist dazu nötig!

Zweite Aufgabe für Dich: Die Konsolenausgaben vereinheitlichen.

Thema geschlossen