Autor Beitrag
OldCat
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Sa 04.12.21 14:58 
Dieser Thread beschäftigt sich mit den Grundlagen zu: "objektorientierter Programmierung", kurz: OOP.

Er findet seinen Ursprung in der Frage: "Wozu in einer (objektorientierten) Programmiersprache denn überhaupt Rückgabewerte notwendig seien."
Rückgabewerte - Wofür brauche ich sie?

Um mir - einem Anfänger - den Sinn von Rückgabewerten vor Augen zu führen, wurde mir vorgeschlagen, mal einen einfachen Taschenrechner zu programmieren.Die Vorgaben dazu waren:
  1. Das Programm soll Rückgabewerte nutzen.
  2. Das Programm soll in Eingabe, Verarbeitung, Ausgabe unterteilt sein.
  3. Die Unterteilung in E.V.A. soll idealerweise in eigenen Klassen und deren Methoden strukturiert sein.

In meinem Code verzichte ich (auf Anraten) auf using Direktiven, um mir als Anfänger nicht die "Herkunft" von Klassen und Methoden der .NET Bibliothek zu verschleiern.
Mein aktueller Quellcode ist noch stark verbesserungswürdig.

Main Class:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
// using System;

// Ich versuche es, Klassen und Methoden modular aufzubauen.
namespace Taschenrechner2
{
    internal class MainClass
    {
        static void Main(string[] args)
        {
            RunProgram myCalc = new RunProgram();
            myCalc.Run();
        }
    }
}


RunProgram Class:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
namespace Taschenrechner2
{
    internal class RunProgram
    {
        private const string AppTitle = "Mini Calculator";
        public void Run()
        {   System.Console.Title = AppTitle;

            System.Console.WriteLine("###############");
            System.Console.WriteLine(AppTitle);
            System.Console.WriteLine("###############\n");

            CalcInput.ChooseInputs();

            System.Console.WriteLine("\nPress any key to exit...");
            System.Console.ReadKey();
        }
    }
}


CalcInput Class:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
namespace Taschenrechner2
{
    internal class CalcInput
    {
        public static void ChooseInputs()
        {
            double result = 0;
            char userDecision;
            do
            {
                System.Console.WriteLine("Please enter your first digit: ");
                double digit1 = double.Parse(System.Console.ReadLine());
                System.Console.WriteLine($"You chose: {digit1}");

                System.Console.WriteLine("Please enter your second digit: ");
                double digit2 = double.Parse(System.Console.ReadLine());
                System.Console.WriteLine($"You chose: {digit2}");

                System.Console.WriteLine(@"Choose your operator by number and hit enter:

> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

                int userChoice = int.Parse(System.Console.ReadLine());

                switch (userChoice)
                {
                    case 1:
                        result = Calculation.Addition(digit1, digit2);
                        break;
                    case 2:
                        result = Calculation.Subtraction(digit1, digit2);
                        break;
                    case 3:
                        result = Calculation.Multiplication(digit1, digit2);
                        break;
                    case 4:
                        result = Calculation.Division(digit1, digit2);
                        break;
                    default:
                        System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                        ChooseInputs();
                        break;
                }
                System.Console.WriteLine(result);

                System.Console.WriteLine("Do you like to solve another arithmetical problem? (y/n)");
                userDecision = char.Parse(System.Console.ReadLine().Trim().ToLower());
            } while (userDecision == 'y');
        }
    }
}


Calculation Class:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
namespace Taschenrechner2
{
    internal class Calculation
    {
        public static double Addition(double x, double y)
        {
            return x + y;
        }

        public static double Subtraction(double x, double y)
        {
            return x - y;
        }

        public static double Multiplication(double x, double y)
        {
            return x * y;
        }

        public static double Division(double x, double y)
        {
            return x / y;
        }
    }
}


Dieser Code hat immer noch mehrere Schwächen:
Zitat:
user profile iconTh69 Es gibt trotzdem noch einen logischen Fehler - und zwar der rekursive Aufruf von ChooseInputs(): gib mal zuerst eine falsche Operatorauswahl ein (z.B. 5) und danach eine richtige, dann beende die Schleife (mit z.B. n) und beobachte die Ausgabe danach.
Um das zu beheben ist es eben sinnvoll, die einzelnen Codeteile in eigene Methoden zu gliedern (und nur das aufzurufen, was man auch haben will - hier also die Eingabe) - aber hier würde es besser sein, die Schleife zu wiederholen, anstatt der Rekursion.

PS: Der char.Parse-Aufruf ergibt eine FormatException, falls der Benutzer mehrere Zeichen (z.B. "yes" oder "no") eingibt und das sollte es wohl nicht - frag einfach das erste Zeichen ab (falls die Eingabe nicht leer ist) - aber wenigstens hast du ja auch hier den Rückgabewert in einer Variablen gespeichert ;-).

Zitat:
user profile iconPalladin007 Allerdings würde ich es aufsplitten.

Z.B. könntest Du den Inhalt der while-Schleife als eigene Methode herauslösen, die dann alles für genau eine Berechnung (Eingaben, Rechnen, Ausgabe) enthält.
Die zweite Methode enthält die while-Schleife, die Abfrage, ob man weiter machen möchte und den Aufruf der Methode mit der eigentlichen Arbeit.
Du erreichst dann zwei kleinere Methoden, die dadurch übersichtlicher sind.


Dann mal an die Arbeit :lupe:


Moderiert von user profile iconTh69: Topic aus C# - Die Sprache verschoben am Sa 04.12.2021 um 15:44
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1265
Erhaltene Danke: 174

Windows 10 x64 Home Premium
C# (Visual Studio Preview)
BeitragVerfasst: Sa 04.12.21 15:03 
Zitat:
Dann mal an die Arbeit


Ja, ran an die Arbeit :D
Wo ist dein Versuch, die Probleme anzugehen? Wo kommst Du nicht weiter, was verstehst Du nicht?
Der Code sieht unverändert aus.
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: So 05.12.21 12:39 
Wünsche allen einen gemütlichen 2. Advent. Ganz unabhängig davon, ob ihr dem Licht der Vernunft oder einem orientalischen Gott folgt :D

Hier mein aktueller Code:

CalcInput Klasse:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
namespace Taschenrechner2
{
    internal class CalcInput
    {
        public static void UserChoiceLoop()
        {
            string userDecision;
            do
            {
                UserInputs();

                System.Console.WriteLine("Do you like to solve another arithmetical problem? (yes/no)");
                userDecision = System.Console.ReadLine().Trim().ToLower();
            } while (userDecision.StartsWith('y'));
        }

        private static void UserInputs()
        {
            System.Console.WriteLine("Please enter your first digit: ");
            double digit1 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit1}");

            System.Console.WriteLine("Please enter your second digit: ");
            double digit2 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit2}\n");

            CalcChoice(digit1, digit2);
        }

        private static void CalcChoice(double digit1, double digit2)
        {
            double result = 0;

            System.Console.WriteLine(@"Choose your operator by number and hit enter:

> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

            int userChoice = int.Parse(System.Console.ReadLine());

            switch (userChoice)
            {
                case 1:
                    result = Calculation.Addition(digit1, digit2);
                    break;
                case 2:
                    result = Calculation.Subtraction(digit1, digit2);
                    break;
                case 3:
                    result = Calculation.Multiplication(digit1, digit2);
                    break;
                case 4:
                    result = Calculation.Division(digit1, digit2);
                    break;
                default:
                    System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                    break;
            }

            // System.Console.WriteLine(result);
            CalcOutput.OutputValue(result);
        }

        //        public static void ChooseInputs()
        //        {
        //            double result = 0;
        //            string userDecision;
        //            do
        //            {
        //                System.Console.WriteLine("Please enter your first digit: ");
        //                double digit1 = double.Parse(System.Console.ReadLine());
        //                System.Console.WriteLine($"You chose: {digit1}");

        //                System.Console.WriteLine("Please enter your second digit: ");
        //                double digit2 = double.Parse(System.Console.ReadLine());
        //                System.Console.WriteLine($"You chose: {digit2}");

        //                System.Console.WriteLine(@"Choose your operator by number and hit enter:

        //> 1.) Addition
        //> 2.) Subtraction
        //> 3.) Multiplication
        //> 4.) Division");

        //                int userChoice = int.Parse(System.Console.ReadLine());

        //                switch (userChoice)
        //                {
        //                    case 1:
        //                        result = Calculation.Addition(digit1, digit2);
        //                        break;
        //                    case 2:
        //                        result = Calculation.Subtraction(digit1, digit2);
        //                        break;
        //                    case 3:
        //                        result = Calculation.Multiplication(digit1, digit2);
        //                        break;
        //                    case 4:
        //                        result = Calculation.Division(digit1, digit2);
        //                        break;
        //                    default:
        //                        System.Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
        //                        ChooseInputs();
        //                        break;
        //                }
        //                System.Console.WriteLine(result);
        //                // CalcOutput.OutputValue(result);

        //                System.Console.WriteLine("Do you like to solve another arithmetical problem? (yes/no)");
        //                userDecision = System.Console.ReadLine().Trim().ToLower();
        //            } while (userDecision.StartsWith('y'));

        //        }
    }
}


CalcOutput Klasse:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
namespace Taschenrechner2
{
    internal class CalcOutput
    {
        public static void OutputValue(double result)
        {
            System.Console.WriteLine(result);
        }
    }
}
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1265
Erhaltene Danke: 174

Windows 10 x64 Home Premium
C# (Visual Studio Preview)
BeitragVerfasst: Mo 06.12.21 08:33 
Was für einen Sinn hat die CalcOutput-Klasse?

Und wenn Du direkt mit den Prinzipien starten willst, dann ließ dich mal in das Single-responsibility principle ein.
Es gibt viele teils sehr verschiedene Erklärungen (nur eine Aufgabe, ein Grund sich zu ändern, eine Verantwortung, etc.), sie alle meinen aber grob das gleiche.
Das betreffend gibt's bei dir mehrere Probleme, ob sie für einen blutigen Anfänger relevant sind - naja, kann man drüber streiten, ich halte das SRP für so essenziell, zu früh geht nicht ^^
SRP ist übrigens Teil der S.O.L.I.D.-Prinzipien, alle sehr wichtig, aber die anderen sind noch schwerer zu verstehen und mMn. nicht ganz so super wichtig.

Die Probleme kann man aber von verschiedenen Winkeln betrachten, ich versuche es mal von einem anderen (nicht SRP) Winkel:
Stell dir immer vor, jemand Fremdes ließt den Code einer (!!!) Methode, will nicht immer alle anderen Methoden nachschauen (bei großen Projekten geht das gar nicht).
Besagter Fremder muss die Methode also ohne Hilfe und nur anhand der Namen verstehen können.

Was würde der Fremde denken, tut die UserInputs-Methode?
Ich würde nicht auf die Idee kommen, dass diese Methode auch noch berechnet ;)
Ich persönlich würde sie aufteilen: Eine Methode, die einen Wert abfragt und per Parameter stellst Du ein, wie dieser Wert in der Konsole heißen soll.
Das ist dann auch direkt nützlich, wenn Du dafür sorgen willst, dass dein Programm nicht abstürzt, wenn der Nutzer "a" statt "1" eintippt, das musst Du dann nur einmal machen und nicht zwei Mal.

Und die CalcChoice-Methode: Wie soll sie denn berechnen und was passiert damit? Das verrät der Name nicht.
Richtig heißen müsste sie also "AskForChoiceAndCalcResultAndPrintToConsole", was direkt sehr viel schlechter zu lesen ist und - spoiler - auch nicht der optimale Weg.
Da versteckt sich auch direkt ein sehr oft passender Indiz für verletztes SRP: "And" im Namen, zu lange Namen oder generell Schwierigkeiten, einen einfach verständlichen Namen zu finden.

Besser wäre hier, Du teilst wieder auf:
  • Eine Methode zum Abfragen der Auswahl und eine zum Berechnen des Ergebnisses
  • Eine Methode zum Berechnen der Eingaben mit der gewählten Art
  • Eine Methode zum Ausgeben des Ergebnisses


Da hast Du dann auch gleich ein paar mehr Rückgabenwerte

PS:

"UserChoiceLoop" ist auch kein guter Name.
Bei so einem Projekt würde ich den Inhalt einfach in der Main-Methode lassen.
Eine beliebige Namenskonvention ist aber auch, dass die Klasse "Application" und die Methode "Run" heißt.
Bei solchen Methoden kann man keinen klaren Namen wählen, deshalb betrachtet man es von weiter oben, mit dem Hintergedanken, dass der Leser gar nicht genau wissen muss, was darin passiert bzw. so oder so nachlesen muss.
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Mo 06.12.21 11:43 
Hey Palladin007,

danke für Deine weiterführende Hilfe :beer:

Bevor ich Deine Vorschläge umsetze, möchte ich sichergehen, dass ich Dich auch verstanden habe. Daher werde ich jetzt noch einmal eine paar Fragen dazu stellen, bzw. Deinen Text noch einmal durchgehen:

Zitat:
Was für einen Sinn hat die CalcOutput-Klasse?

Aus Deiner Frage leite ich ab, dass die CalcOutput Klasse unnötig ist. ^_^ Als Asperger Authist weiß ich allerdigns nicht, ob diese Frage nur ein Hinweis, eine rethorische Frage oder eine Frage aus Interesse ist. Aufgrund meiner Beschaffenheit beantworte ich alle Fragen grundsätzlich, auch wenn sie nur rethorischer Natur sind:
Die CalcOutput Klasse habe ich geschrieben, weil die Ausgabe von Eingabe und Verarbeitung getrennt sein soll(te). Aber natürlich, der Code kommt auch ohne eine eigene Output-Klasse aus...
Ganz am Anfang meiner Reise hier in dem Forum, als die Taschenrechneraufgabe aus der Taufe gehoben wurde, hieß es als Vorgabe: Eingabe, Verarbeitung und Ausgabe sollen in jeweils einer Klasse erfolgen. Das habe ich jetzt umgesetzt. Oder versucht umzusetzen :gruebel:

Zitat:
Und wenn Du direkt mit den Prinzipien starten willst, dann ließ dich mal in das Single-responsibility principle ein.
Es gibt viele teils sehr verschiedene Erklärungen (nur eine Aufgabe, ein Grund sich zu ändern, eine Verantwortung, etc.), sie alle meinen aber grob das gleiche.
Das betreffend gibt's bei dir mehrere Probleme, ob sie für einen blutigen Anfänger relevant sind - naja, kann man drüber streiten, ich halte das SRP für so essenziell, zu früh geht nicht ^^

Klar, "Regeln und Konventionen" lernen sollte ich defintiv so früh wie möglich. Je länger ich das falsch mache, desto schwerer ist es nacher, sich umzugewöhnen. Daher können wir mit "Clean Code Konventionen" gerne auch jetzt schon starten.
Ich weiß nicht mehr, ob Du es warst, oder Ralf Jansen, aber einer von euch beiden hat mir ja schon mal das Buch "Clean Code" empfohlen und mir einen Link von David Tielke auf Youtube über das Thema geschickt.
Im Grunde habe ich auch versucht, das Ganze SRP mäßig zu schreiben. Werde mich damit aber noch mal eingehnder befassen.

Zitat:
Was würde der Fremde denken, tut die UserInputs-Methode?
Ich würde nicht auf die Idee kommen, dass diese Methode auch noch berechnet

Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas? :gruebel: Hier bräuchte ich noch mal eine Hilfestellung.

Zitat:
Ich persönlich würde sie aufteilen: Eine Methode, die einen Wert abfragt und per Parameter stellst Du ein, wie dieser Wert in der Konsole heißen soll.
Hier stehe ich ebenfalls auf dem Schlauch :hair:

Zitat:
Das ist dann auch direkt nützlich, wenn Du dafür sorgen willst, dass dein Programm nicht abstürzt, wenn der Nutzer "a" statt "1" eintippt, das musst Du dann nur einmal machen und nicht zwei Mal.

Oh ja, das hab ich ganz vergessen. Danke, werde ich ändern, sodass kein Crash an dieser Stelle mehr vorkommt. Nur das mit dem "Aufteilen" (das quote zuvor), hab ich nicht so ganz verstanden.

Zitat:
Und die CalcChoice-Methode: Wie soll sie denn berechnen und was passiert damit? Das verrät der Name nicht.
Richtig heißen müsste sie also "AskForChoiceAndCalcResultAndPrintToConsole", was direkt sehr viel schlechter zu lesen ist und - spoiler - auch nicht der optimale Weg.
Da versteckt sich auch direkt ein sehr oft passender Indiz für verletztes SRP: "And" im Namen, zu lange Namen oder generell Schwierigkeiten, einen einfach verständlichen Namen zu finden.
Ah, der zweite Absatz ist ja interessant. :think: CalcChoice() hat mir selber auch schon nicht gefallen. Dasselbe gilt für UserChoiceLoop().
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4601
Erhaltene Danke: 956


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 06.12.21 13:30 
Zitat:
Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas?

Sie ruft die entsprechende Methode direkt auf daher kann man das so bezeichnen das sie auch berechnet. Eine echte (wünschenswerte) Trennung der Aspekte (Eingabe und Berechnung) bekommst du hin wenn du eine dritte Methode benutzt. Also eine Methode die die anderen beiden Methoden für Input und Berechnung nacheinander aufrufst. Also den Rückgabewert von Input über diese dritte Methode als Parameter an die Methode zum Berechnen weiterreichst. Dann sind Eingabe und Berechnung wirklich unabhängig voneinander was sie bei dir gerade nicht sind.

Wir hatten schonmal über den entsprechenden Pseudocode gesprochen. Hier nochmal mit den von dir benutzten Bezeichnern.

ausblenden C#-Quelltext
1:
2:
3:
var userInputs = CalcInput.UserInputs();
var result = CalcInput.CalcChoice(userInputs);
CalcOutput.OutputValue(result);
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4528
Erhaltene Danke: 996

Win10
C#, C++ (VS 2015/17/19)
BeitragVerfasst: Mo 06.12.21 13:54 
Dann sollte die Berechnugnsmethode aber besser gleich in Calculation rein.

Für Ein- und Ausgabe reicht es aber, wenn diese in einer eigenen Klasse (bzw. Methoden direkt der Hauptklasse RunProgram [wenn auch der Name nicht so toll ist]) untergebracht sind.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1265
Erhaltene Danke: 174

Windows 10 x64 Home Premium
C# (Visual Studio Preview)
BeitragVerfasst: Mo 06.12.21 14:01 
user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Die CalcOutput Klasse habe ich geschrieben, weil die Ausgabe von Eingabe und Verarbeitung getrennt sein soll(te). Aber natürlich, der Code kommt auch ohne eine eigene Output-Klasse aus...

Der Sinn ist korrekt, aber die Umsetzung?
Schau dir den restlichen Code an, ist da irgendetwas getrennt?
Nö, ist es nicht und für die eine Zeile ist diese Klasse schlicht unnötig.
Wenn Du die UI trennst, dann richtig und überall, sonst bringt das Ganze nichts.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Im Grunde habe ich auch versucht, das Ganze SRP mäßig zu schreiben. Werde mich damit aber noch mal eingehnder befassen.

Das ist das Problem bei diesen sehr abstrakten Prinzipien: Nur weil Du glaubst, es verstanden zu haben, ist die Lücke zum echten Verständnis oft noch sehr groß.
Es gibt noch das Buch "Clean Architecture" von Robert C. Margin, das Buch ist mMn. sehr gut, allerdings sage ich das mit meinem Vorwissen. Wie gut das ein Anfänger versteht, kann ich nicht sagen.
Tatsächlich hat seine Erklärung zum SRP bei mir noch eine letzte Schuppe von den Augen gelöst, nämlich was mit "Verantwortung" wirklich gemeint ist und das ist nicht immer (oft, aber nicht immer) deckungsgleich mit "Aufgabe".
Aber wie gesagt: Es kann sein, dass Du damit deine Probleme haben wirst.

Bis dahin reicht es, wenn Du versuchst, die "Tätigkeiten" einer Methode zu erkennen - ausgehend vom Code nur dieser einen Methode.
Bei der "UserInputs"-Methode sind es drei Tätigkeiten: Zahl 1 erfragen, Zahl 2 erfragen, Berechnen. Natürlich kann man das alles weiter aufbrechen oder zusammenfassen, wie man sich die Tätigkeiten zurecht
Wenn Du das hast, kannst Du dir überlegen, wie Du weiter machst und in diesem Fall bietet es sich eben an, das Erfragen von Zahlen auszulagern.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Hm, UserInputs() fragt den User nach Eingabe der Ziffern die berechnet werden sollen, speichert diese ab und ruft dann eine neue Methode auf (CalcChoice()). Wo berechnet sie denn etwas? :gruebel: Hier bräuchte ich noch mal eine Hilfestellung.

Wie Ralf schon schreibt:
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Sie ruft die entsprechende Methode direkt auf daher kann man das so bezeichnen das sie auch berechnet.

Ich versuche es nochmal aus einem anderen Blickwinkel:
Wenn das Programm die Methode ausgeführt hat - was wurde in diesem einen Schritt (die Methode ausführen) alles gemacht?
Wenn jemand Anderes den Code ließt und "UserInputs" ließt, dürfte der ziemlich überrascht sein, wenn plötzlich auch noch nach einer Berechnungsart gefragt und berechnet wird. Und genau diese "Hä?"-Momente willst Du vermeiden.

user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Hier stehe ich ebenfalls auf dem Schlauch

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
double AskForInputNumber(int numberId)
{
    // Text anzeigen
    // Eingabe erfragen
    // Eingabe parsen
    // Ergebnis zurück geben
}


user profile iconOldCat hat folgendes geschrieben Zum zitierten Posting springen:
Ah, der zweite Absatz ist ja interessant. :think: CalcChoice() hat mir selber auch schon nicht gefallen. Dasselbe gilt für UserChoiceLoop().

Prinzipiell gilt das auch für UserChoiceLoop, ja. Allerdings kann man das nicht ewig so weiter treiben, irgendwann hast Du eine Methode, die mehrere andere aufruft und dann hast Du ein Problem.
Gedanklich musst Du dir das wie einen Baum vorstellen: Eine Komponente (Methoden, Klassen, Projekte, etc.) benutzt immer mehrere andere Komponenten.
Wenn Du dir nun diesen Baum aufbaust, hast Du am Ende die tatsächliche Logik, davor etwas andere Logik, die die darunter liegende Logik aufruft und so weiter.
Bis zum Anfang wird es immer abstrakter, weil sich hinter einem Methodenaufruf immer mehr verbirgt, bis ganz nach oben: Die Main-Methode ist die Wurzel des Baums, die ruft alles andere auf.
Die Benennung sollte ungefähr ähnlich ticken, auf oberster Ebene kannst Du nicht jedes Detail von ganz unten betrachten, aber Du kannst den Komponenten in der tieferen Schicht Namen geben und die dann als Einheit betrachten.

Beispiel:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Program.Main()
    Application.Run()
        Loop
            AskForInput()
            CalcChoice()
                AskForChoice()
                Calculate()
            OutputValue()
        AskWantToContinue()


Das soll nicht akkurat sein, sondern nur ein Beispiel für die Hierarchie sein.
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mo 06.12.21 22:34 
Der Pseudocode von @Palladin007 ist doch ein Paradebeispiel für das S von SOLID.

Jeder Methodenname in dieser Abstraktion sagt genau eine Operation an. AskForInput fragt nach Eingaben (welche, sieht man nicht auf Anhieb). CalcChoice fragt nach den Operatoren. Calculate berechnet irgendwas. OutputValue spuckt n Wert aus. AskWantToContinue... You got it.

Ohne auch nur eine Zeile Code gesehen zu haben, kann man nur anhand der Methodennamen bereits schließen, das hier womöglich ein Taschenrechner programmiert wurde.

Jede dieser Methoden macht dann genau das, was der Name aussagt. Deine bisherigen Methoden, @Oldcat, haben oftmals mehr als eine Aufgabe: Abfragen des Userinpputs und Berechnen, oder Berechnen und Ausgabe. Das und muss da weg ;)

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.

Für diesen Beitrag haben gedankt: Palladin007
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Di 07.12.21 12:43 
Hey ihr lieben :beer:

Ein paar Fragen habe ich immer noch :oops: , die mir beim Programmieren der neuen "Version" des Mini-Calculators den Gedankenfluss blockieren:

Problem 1.1:
Es wurde geschrieben, dass eine Methode nur eine Aufgabe erfüllen soll. Klingt einfach. Eine Methode erfüllt aber schon mehr als eine Aufgabe, wenn sie eine andere Methode aufruft (die dann logischerweise eine andere Aufgabe erfüllt), richtig?

So bei meinem bisherigen Code (Das ist nicht meine neuer Code), namentlich die UserInputs() Methode:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        private static void UserInputs()
        {
            System.Console.WriteLine("Please enter your first digit: ");
            double digit1 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit1}");

            System.Console.WriteLine("Please enter your second digit: ");
            double digit2 = double.Parse(System.Console.ReadLine());
            System.Console.WriteLine($"You chose: {digit2}\n");

            CalcChoice(digit1, digit2);
        }



Problem 1.2:
Das Schreiben einer Methode, dass Methoden aufruft, würde das Problem umgehen. Wenn ich das richtig verstanden habe, sollte ich aber niemals Methoden schreiben, die nur dazu dienen Methoden aufzurufen, oder? Wenn dem so ist, beißt sich meine innere Katze in den Schwanz/ich drehe mich gedanklich im Kreis und komme nicht weiter.


Problem 2:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Program.Main()
    Application.Run()
        Loop
            AskForInput()
            CalcChoice()
                AskForChoice()
                Calculate()
            OutputValue()
        AskWantToContinue()

Bin mir ja immer nicht sicher, ob ich den wirklich so lese, wie ich es lesen sollte. Daher ein paar Fragen:
  • Es existieren nach diesem Modell nur zwei Klassen (statt der fünf Klassen, wie bisher)?
  • Die Methode Run() in der Application class startet den Rest der Logik, welche auch vollständig in der Application class geschrieben wird?

Ich habe noch mehr Fragen dazu, aber diese könnten sich möglicherweise durch die Antworten auf die jetzt gestellten Fragen erübrigen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4601
Erhaltene Danke: 956


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 07.12.21 13:43 
Zitat:
Problem 1.1:
Es wurde geschrieben, dass eine Methode nur eine Aufgabe erfüllen soll. Klingt einfach. Eine Methode erfüllt aber schon mehr als eine Aufgabe, wenn sie eine andere Methode aufruft (die dann logischerweise eine andere Aufgabe erfüllt), richtig?


Es ist ein kleiner Unterschied was man anstreben sollte und was tatsächlich erreichbar ist ;) Der Satz "eine Methode macht genau eine Sache" ist genau sowas. Man sollte es anstreben auch wenn es nie wirklich erreichbar ist außer die gesamte Anwendung tut selbst auch nur eine Sache. Ziel der Trennung ist halt auch Dinge voneinander so zu trennen das sie austauschbar werden und das wird umso schwerer umso mehr eine Methode tut. Du kannst es möglicherweise ein wenig wie Lego betrachten. Es gibt Methoden die genau ein Legosteinchen darstellen aber um irgendwas zu erreichen muß man aber mehrerer zusammenstecken. Die einzelnen Legosteinchen sind dann aber weiterhin elementar und weitestgehend belibieg austauschbar ohne das das zusammengebaute auseinanderfällt. Das Ding Dinge austauschen zu können ohne das andere betroffen sind ist ein der zentralen Ziele solcher Konstrukte.

Irgendwer hat hier das EVA Prinzip erwähnt oder eingeführt. Das kann in der Größenordnung deiner Anwendung der Masstab sein. Mindest diese 3 Teile sollten scharf voneinander getrennt sein (einzelne Legosteine) und nur von Methoden zusammengeführt werden so das du zumindest theoretisch die 3 Einzelteile austauschen kannst ohne das die anderen Beteiligten irgendwie angefasst oder geändert werden müßten.

Zitat:
Die Methode Run() in der Application class startet den Rest der Logik, welche auch vollständig in der Application class geschrieben wird?


Die Zeile kannst du erstmal ignorieren. "Application.Run()" hilft gerade nicht du kannst die Loop auch direkt aus der Main der Programm Klasse starten.
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Mi 08.12.21 19:06 
Momentan stecke ich sowas von fest. :eyecrazy:

Es ist das erste Mal, dass ich mich gezwungen sehe, (wenn ich die Vorgaben richtig interpretiere), Übergabeparameter an die Main() Methode zu übergeben (double digit1, double digit2). Das lässt der Compiler nicht zu.

Ich folge den Vorgaben:

  • ausblenden C#-Quelltext
    1:
    2:
    3:
    4:
    5:
    6:
    7:
    8:
    9:
    Program.Main()
        Application.Run()
            Loop
                AskForInput()
                CalcChoice()
                    AskForChoice()
                    Calculate()
                OutputValue()
            AskWantToContinue()

  • Methoden werden nicht von anderen Methoden aufgerufen, es sei denn von einer Methode, die nur Methoden aufruft, oder über Rückgabewerte.


Folgende Methoden habe ich nochmal umbenannt aus dem Beispiel: AskForInput() = AskForDigits(); AskForChoice() = AskForOperator(); Die Calculate() Methode aus dem Beispiel sind die vier Methoden der vier Grundrechenarten, wie gehabt.

Mein Code funktioniert nicht, so sieht er aus aktuell aus:

Der Code besteht aus zwei Klassen: Program.Main() und Application.Run(). In Main() erstelle ich das Objekt myCalc und rufe den Member Run() auf.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Application myCalc = new Application();
            myCalc.Run();
        }
    }
}

In Application.Run() soll die do while() Schleife starten, aktuell noch auskommentiert. Innerhalb der Schleife wird private void AskForDigits() aufgerufen, (diese fragt nur noch nach den Operanden und speichert diese ab.)
Die Methode CalcChoice() soll die AskForOperator() Methode aufrufen. In ihr werden über die switch case Anweisung weiterhin die Methoden der vier Grundrechenarten aufgerufen und deren Rückgabewerte in result gespeichert. (Hier fange ich an, Bauchschmerzen zu bekommen.) AskForOperator braucht die Übergabeparameter (double, double) -> wird von CalcChoice(couble, double) aufgerufen, die innerhalb der do while() Schleife von Run() aufgerufen wird. Dafür braucht Run() ebenfalls die Übergabeparameter (double, double) -> Und dafür braucht dann die Main() Methode die Parameter (double, double)...Und hier sind meine Bauchschmerzen dann ein konkreter Fehler. Der Compiler erkennt keinen Einsteigspunkt mehr:

Application.cs:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
namespace MiniCalculator
{
    internal class Application
    {
        public void Run(double digit1, double digit2)
        {
            //do
            //{
            AskForDigits();
            CalcChoice(digit1, digit2);
            //}while ();
            System.Console.WriteLine("Press any key to exit...");
            System.Console.ReadKey();
        }

        private void AskForDigits()
        {
            System.Console.WriteLine("Please type in your first digit.");
            double digit1 = int.Parse(System.Console.ReadLine());

            System.Console.WriteLine("Please type in your second digit.");
            double digit2 = int.Parse(System.Console.ReadLine());
        }

        private void CalcChoice(double digit1, double digit2)
        {
            AskForOperator(digit1, digit2);
        }

        private void AskForOperator(double digit1, double digit2)
        {
            System.Console.WriteLine(@"Please choose an operator by number and hit enter:
> 1.) Addition
> 2.) Subtraction
> 3.) Multiplication
> 4.) Division"
);

            int userChoice = int.Parse(System.Console.ReadLine());
            double result;

            switch (userChoice)
            {
                case 1:
                    result = CalculateAddition(digit1, digit2);
                    break;
            }
        }

        private double CalculateAddition(double a, double b)
        {
            return a + b;
        }

        private double CalculateSubtraction(double a, double b)
        {
            return a - b;
        }

        private double CalculateMultiplication(double a, double b)
        {
            return a * b;
        }

        private double CalculateDivision(double a, double b)
        {
            return a / b;
        }
    }
}


Program.cs:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main(double digit1, double digit2)
        {
            Application myCalc = new Application();
            myCalc.Run(digit1, digit2);
        }
    }
}


Fehler CS5001 Das Programm enthält keine als Einstiegspunkt geeignete statische Main-Methode.
Warnung CS0028 "Program.Main(double, double) hat ein falsche Signatur, um ein Einstiegspunkt zu sein.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4601
Erhaltene Danke: 956


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 08.12.21 19:19 
Zitat:
Fehler CS5001 Das Programm enthält keine als Einstiegspunkt geeignete statische Main-Methode.


Die main hatte entweder keine Parameter oder einen string[] Parameter (üblicherweise namens args). Alles andere ist keine gültige Signatur. Wie man Parameter an eine Konsolen Anwendung übergibt findest du sicher in der Dokumentation zur main Methode.

Aber warum willst du das? Du möchtest doch nach den Operatoren fragen und sie nicht schon an dein Programm übergeben.
Also entweder als Kommandozeilenparameter an das Programm übergeben (dann über besagten args Parameter der main Methode) oder danach fragen aber doch nicht beides :nixweiss:

AskForDigit schreibt die Eingaben in lokale Variablen die sind nach dem Methodenaufruf vergessen. Du musst die aus der Methode rausgeben (wie oft hatten wir das jetzt schon das du die Dinge auch wieder aus den Methoden rausholen musst?). Nur weil Variablen gleich heißen sind das nicht die gleichen Variablen. digit1, digit2 haben in Application-Run nichts mit den gleichnamigen Variablen in AskForDigits zu tun.
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mi 08.12.21 23:15 
Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Die statische Main-Methode ist bereits der Einsprungpunkt - also quasi dein .Run() ;) Die Main-Methode ist somit der zentrale Punkt, von wo aus alles gesteuert wird. Und von hier aus erzeugst du deine Klasse und rufst du auch fleißig deine Methoden eben dieser Klasse auf.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
namespace MiniCalculator
{
    internal class Program
    {
        static void Main()
        {
            MyCalculationClass myCalc = new MyCalculationClass();
            //Abfrage der Eingabewerte hier
            myCalc.AskValues();  //Ich sage jetzt schon, das das NICHT funktionieren wird
            myCalc.AskOperator();
            //usw. usf.
        }
    }
}


Siehst du, wie das funktionieren soll mit der Main-Methode ?

Manchmal wäre ein Pair-Programming gar nicht schlecht...

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1265
Erhaltene Danke: 174

Windows 10 x64 Home Premium
C# (Visual Studio Preview)
BeitragVerfasst: Do 09.12.21 00:19 
Zitat:
Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Gibt's schon - wenn man sowas braucht, aber das ist hier definitiv nicht der Fall.
Deshalb war mein Baum-Beispiel auch nur ein Beispiel um zu zeigen, was ich mit den Namen meine.

Die Schleife würde ich auch einfach in die Main-Methode legen.


@OlafSt, zum Beispiel:
Ich persönlich bin kein Freund von solchem Vorgehen - zumindest so, wie es im Code aussieht.
Aber bevor ich kritisiere: Meinst Du, dass die Klasse die Ergebnisse der aufgerufenen Methoden (eingegebene Werte, Operator, etc.) als lokalen Zustand behält und in den späteren Methoden (Calculate) benutzt?
Wenn nicht, dann bin ich still :D
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 09.12.21 07:25 
Pscht ;)

Ich will sehen, ob unser angehender Meister-Programmierer mitdenkt :D

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Do 09.12.21 09:26 
Zitat:
Ich will sehen, ob unser angehender Meister-Programmierer mitdenkt :D


Oh... bitte macht euch nicht über mich lustig. Ich komme mir eh schon total minderbemittelt vor und wundere mich, dass ihr überhaupt noch auf meine Probleme eingeht...
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4528
Erhaltene Danke: 996

Win10
C#, C++ (VS 2015/17/19)
BeitragVerfasst: Do 09.12.21 10:51 
Da du sonst bis Weihnachten nicht vorankommst, habe ich jetzt mal das Programm für dich erstellt (auf Grundlage deines bisherigen Codes):
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
using System;

// Program.cs
namespace Calculator
{
    public class Program
    {
        static void Main(string[] args)
        {
            Application app = new Application();
            app.Run();
        }
    }
}

// Application.cs
namespace Calculator
{
    public class Application
    {
        private const string AppTitle = "Mini Calculator";
        
        public void Run()
        {
            Console.Title = AppTitle;

            Console.WriteLine("###############");
            Console.WriteLine(AppTitle);
            Console.WriteLine("###############\n");

            string s;
            
            do
            {
                var userInputs = Input();
                var result = Calculate(userInputs);
                OutputResult(result);

                Console.WriteLine("Do you like to solve another arithmetical problem? (y/n)");
                s = Console.ReadLine().Trim().ToLower();
            } while (s.StartsWith("y"));

            Console.WriteLine("\nPress any key to exit...");
            Console.ReadKey();
        }
        
        public class UserInputs
        {
             public double Number1 { get; set; }
             public double Number2 { get; set; }
             public int Operator { get; set; }
        }

        public static UserInputs Input()
        {
            Console.WriteLine("Please enter your first number: ");
            double number1 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number1}");

            Console.WriteLine("Please enter your second number: ");
            double number2 = double.Parse(Console.ReadLine());
            Console.WriteLine($"You chose: {number2}");

            Console.WriteLine("Choose your operator by number and hit enter:");
            Console.WriteLine("> 1.) Addition");
            Console.WriteLine("> 2.) Subtraction");
            Console.WriteLine("> 3.) Multiplication");
            Console.WriteLine("> 4.) Division");

            int op = 0;
                
            while (true)
            {
                op = int.Parse(Console.ReadLine());
                Console.WriteLine($"You chose: {op}");

                if (op < 1 || op > 4)
                {
                    Console.WriteLine("Your input is invalid. Try again, with numerals 1 - 4.");
                    continue;
                }
                    
                break;
            }
                
            return new UserInputs() { Number1 = number1, Number2 = number2, Operator = op };
        }
        
        public double Calculate(UserInputs userInputs)
        {
            double result = 0;
          
            switch (userInputs.Operator)
            {
                case 1:
                    result = Calculation.Addition(userInputs.Number1, userInputs.Number2);
                    break;
                case 2:
                    result = Calculation.Subtraction(userInputs.Number1, userInputs.Number2);
                    break;
                case 3:
                    result = Calculation.Multiplication(userInputs.Number1, userInputs.Number2);
                    break;
                case 4:
                    result = Calculation.Division(userInputs.Number1, userInputs.Number2);
                    break;
            }
            
            return result;
        }
        
        public void OutputResult(double result)
        {
            Console.WriteLine($"Result: {result}");
        }
    }


// Calculation.cs
namespace Calculator
{
    public static class Calculation
    {
        public static double Addition(double x, double y)
        {
            return x + y;
        }

        public static double Subtraction(double x, double y)
        {
            return x - y;
        }

        public static double Multiplication(double x, double y)
        {
            return x * y;
        }

        public static double Division(double x, double y)
        {
            return x / y;
        }
    }
}

Ich habe alle Namen in englisch gehalten, die explizite Angabe des Namensbereiches System jedoch bei den Aufrufen entfernt.

Hier noch als lauffähiger Code: Ideone-Code: Calculator (mit vorgegebenem User Input)

Der Code ist (aus professioneller Sicht) noch verbesserungsfähig, aber bitte versuche ihn zu verstehen und wenn nicht dann stelle explizite Fragen zu einzelnen Codeteilen.

Für diesen Beitrag haben gedankt: OldCat
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Do 09.12.21 10:54 
Zitat:
Die main hatte entweder keine Parameter oder einen string[] Parameter (üblicherweise namens args). Alles andere ist keine gültige Signatur. Wie man Parameter an eine Konsolen Anwendung übergibt findest du sicher in der Dokumentation zur main Methode.

Aber warum willst du das?

Laut Microsoft.docs sind keine beliebigen Parameter in Main() erlaubt, außer Main(string[] args):

Zitat:
The following list shows valid Main signatures:

public static void Main() { }
public static int Main() { }
public static void Main(string[] args) { }
public static int Main(string[] args) { }
public static async Task Main() { }
public static async Task<int> Main() { }
public static async Task Main(string[] args) { }
public static async Task<int> Main(string[] args) { }


Auch als Anfänger leiste ich mir jetzt mal ne Meinung ^_^: Ich finde, in der Main() Methode einer Konsolenanwendung sollte so wenig Code wie möglich stehen. Alles in eine Main() Methode klatschen ist doch gegen S.O.L.I.D.? Dann brauche ich auch keine Rückgabewerte oder Ein-/Übergabeparameter mehr. Ich hab hier älteren Code, vor unserer "Zusammenarbeit". Auch einen Taschenrechner. Der Code ist Müll. Er funktioniert, ist aber Müll: Alles steht dort in der Main() Methode und alle Methoden sind void und besitzen keine Ein-/Übergabeparameter. Ist ja auch nicht nötig, da eh alles in einer Methode steht: Der Main() Methode eben...
Das die Main() Methode nicht irgendeine Methode ist @OlafSt, sondern DER Einstiegspunkt für das jeweilige Programm, das ist einer der ersten Dinge, die ich lernte. Für mich ist Main() "untouchable". Hier will ich schon gar nicht irgendwelche Parameter einfügen. Was ja auch gar nicht erlaubt ist, laut Microsoft.docs, (siehe oben).

Zitat:
user profile iconOlafSt Application und .Run() gibt es nur in GUI-Programmen (WinForms, WPF). Du aber hast eine Konsolenanwendung - da gibts sowas nicht.

Als Anfänger habe ich bisher eine Sache streng beherzigt: "Fange mit Konsolenanwendungen an und bleibe dort eine Weile, bevor Du Dich entscheidest, etwas anderes zu machen." Aus diesem Grund, habe ich mich bisher nicht mit WPF und Co. beschäftigt. Auch nicht, als ich mir das Buch "C# für Dummies" kaufte, und dort leider mit WinForms gestartet wird, statt mit Konsolenanwendungen. Es scheint mir ein gutes Buch zu sein. Doch liegt es jetzt hier auf meinem Bücherstapel auf meinem Tisch... bis mir mein Inneres ein "Go" für WPF und Co. gibt.

Möglicherweise sind dort die Begriffe "Application" und ".Run()" feste Begrifflichkeiten. In einer Konsolenanwendung sind sie es nicht. Hier kann ich eine Klasse "Application", oder eine Methode Run() nennen, ohne dabei einen syntaktischen Fehler zu machen, oder SOLID zu verletzen.

Zitat:
Manchmal wäre ein Pair-Programming gar nicht schlecht...

Für mich wäre es ein Traum mit jemandem von euch Pair-Programming zu machen. :dance2: Ich denke auch, dass eine ganze Menge an Verständnisproblemen dann auch gar nicht auftreten würden, wie das hier über das Chatten der Fall ist...

Ah, ich sehe gerade, Th69 hat mir was geschrieben.

Moderiert von user profile iconTh69: C#- durch Quote-Tags ersetzt
OldCat Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 77

Win 10 Pro x64
C# (VS 2019), (VS 2022)
BeitragVerfasst: Do 09.12.21 10:55 
user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Da du sonst bis Weihnachten nicht vorankommst, habe ich jetzt mal das Programm für dich erstellt (auf Grundlage deines bisherigen Codes):
...


Danke!

Moderiert von user profile iconTh69: Zitat eingekürzt.