Autor Beitrag
ottto
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 20



BeitragVerfasst: Mo 08.12.14 19:26 
Hallo,
ich bin Neuling in der Welt der objektorientierten Programmierung. Nachdem ich nun ein Grundlagenbuch durchgearbeitet hab, tue ich mich immer noch schwer objektorientiert zu "denken". In folgendem Beispiel werden die CSV-Dateien in einem Verzeichnis ausgelesen, einzelne Werte selektiert und in eine neue Datei geschrieben. Später sollen noch Werte aus der Quelldatei verglichen bzw. verrechnet werden. E/A -Ausnahmen wurden auch noch nicht berücksichtigt.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
    class Program
    {
        static void Main(string[] args)
        {
            string qPfad = @"C:\Ablage";
            string zPfad = @"C:\Ablage\Ziel";
            DirectoryInfo verz1 = new DirectoryInfo(qPfad);
            foreach (FileInfo f in verz1.GetFiles("*.CSV"))
            {
                Console.WriteLine(f.FullName);
                string dateiInhalt = File.ReadAllText(f.FullName);
                string[] arrDateiInhalt = dateiInhalt.Split(';'); 
                string zDatName = f.FullName.Insert(f.FullName.IndexOf(".csv"), "_neu"); 
                zDatName = zDatName.Replace(qPfad, zPfad);
                Console.WriteLine(zDatName);
                File.WriteAllText(zDatName, (arrDateiInhalt[3] + ";" + arrDateiInhalt[1] + ";"));
            }
            Console.ReadKey();
        }
    }


Bisher sieht mein Programm immer noch "prozedural" aus. Sollte an der Stelle Logik in Klassen/Methoden ausgelagert werden? kann mir mal bitte jemand einen Schups geben?
Danke.
ottto

Moderiert von user profile iconTh69: Quote- durch C#-Tags ersetzt
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 08.12.14 20:05 
Hallo,

der erste Schritt wäre, daraus eine Methode zu erstellen, welche die beiden Pfade als Parameter enthält.
Und anschließend kannst du diese Methode dann in eine eigene Klasse (und am besten in einer eigenen Datei) auslagern.

Das Visual Studio kann dir sogar beim Erstellen der Methode helfen, indem du die entsprechenden Codezeilen selektierst und dann im Kontextmenü Extract Method (bzw. auf deutsch Methode extrahieren) auswählst.
Damit du aber lernst, wie man Methoden erzeugt, solltest du es einmal selber von Hand machen (aber beim weiteren Programmieren kann der Menüpunkt sehr hilfreich sein, da auch automatisch die Parameter erzeugt werden).

Für diesen Beitrag haben gedankt: ottto
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 08.12.14 20:58 
Ein zentraler Punkt der vielleicht hilft in die ~richtige~ Richtung zu denken ist insbesondere Wiederverwendbarkeit zu berücksichtigen. Du hast jetzt einen Einmalcode geschrieben der genau einen bestimmten Spezialfall abdeckt. Du kannst nicht einen Teil deines Codes nehmen und in anderem Context wiederverwenden. Insbesondere nicht so das du eine gewisse Sicherheit hast das er auch funktioniert ohne es genau zu testen.

Um etwas wiederzuverwenden brauchst du einen scharf abgrenzbare einzelne Aufgabe die dieser Baustein (also z.B eine Klasse) löst. Bei deinen Stück Code müsstest du dir also überlegen welcher Teilaufgaben muss ich lösen (Datei in Format xyz einlesen, Daten verarbeiten, Daten als Datei in Format abc wegschreiben etc.). Als nächsten Schritt überlegst du dir dann welche dieser Teilaufgaben werde ich wohl in Zukunft so oder ähnlich immer wieder mal lösen müssen und welche Teile sind eigentlich so speziell das die nur in diesem konkreten Fall hilfreich sind. Ersteres wären also Kandidaten für eigene Klassen/Methoden und 2.teres eher die Gluelogik die die Klassen zu einen Programm zusammenführt.

Ein Beispiel, bei dir ist doch eindeutig das das CSV lesen und schreiben ein wiederverwendbarer Baustein ist. Damit er wiederverwendbar wird musst du dann überlegen welche Eigenschaften sind fix und welche Variable. Umgesetzt in eine Klasse hieße das dann du musst der irgendwie den Pfad zur Datei zukommen lassen und das gewünschte Trennzeichen einstellen können (und dann irgendwann vielleicht weitere Dinge wie Quotings etc.). Damit man die eingelesen Daten auch irgendwie bearbeiten kann, wenn wir wieder an Wiederverwendbarkeit denken so das die austauschbar wäre ohne das Funktionieren des Rests zu gefährden, brauchst du eine Datenstruktur an die die die Bearbeitung einfach dranschrauben kannst. Das kann erstmal ein string Array sein je nachdem was du dir als Bearbeitung der Daten vorgestellt hast kann das aber zu knapp sein. Das wäre hier also möglicherweise eine 2.te Klasse. Ergo hättest du am Ende eine Klasse/Methode die ein CSV einlesen kann und ein bearbeitbare Datenstruktur (eigene Klasse) zurückliefert und eine Klasse/Methode (könnte auch eine andere Methode der zuerst genannten Klasse sein) die die Datenstruktur entgegennimmt und in ein Datei wegschreibt.

Für diesen Beitrag haben gedankt: ottto
ottto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 20



BeitragVerfasst: Do 11.12.14 17:05 
Hallo,
und vielen Dank für Eure hilfreichen Antworten!
Anbei meine objektorientierter Ansatz bei dem auch die Wiederverwendbarkeit berücksichtigt wurde.


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 CSV
{
    class Program
    {
        static void Main(string[] args)
        {
            string qPfad = @"C:\Ablage";
            string zPfad = @"C:\Ablage\Ziel";

            CSV_read_write csv_r_w = new CSV_read_write();
            csv_r_w.qPfad = qPfad;
            csv_r_w.zPfad = zPfad;
            DirectoryInfo qVerz = new DirectoryInfo(qPfad);
            foreach (FileInfo f in qVerz.GetFiles("*.CSV"))
            {
                csv_r_w.qDatName = f.FullName;
                // Ausgabestring bauen
                string rueckgabe = csv_r_w.Lesen();
                string zAusgabe = ("Testausgabe: " + rueckgabe);
                csv_r_w.Schreiben(zAusgabe);
            }
            Console.ReadKey();
        }
    }
}


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:
26:
27:
namespace CSV
{
    class CSV_read_write
    {
        // Eigenschaften
        public string qPfad { get; set; }
        public string zPfad { get; set; }
        public string qDatName { get; set; }

        // Methoden
        public string Lesen()
        {
            //EA-Ausnahme fehlt noch!
            string dateiInhalt = File.ReadAllText(qDatName);
            string[] arrDateiInhalt = dateiInhalt.Split(';');
            return (arrDateiInhalt[1]); //??? Array übergeben???
        }
        public void Schreiben(string text)
        {
            string zDatName = qDatName.Insert(qDatName.IndexOf(".csv"), "_neu");
            zDatName = zDatName.Replace(qPfad, zPfad);
            Console.WriteLine(zDatName);
            //EA-Ausnahme fehlt noch!
            File.WriteAllText(zDatName, text);
        }
    }
}


Ich würde gern das komplette Array "arrDateiInhalt" aus der Methode Lesen übergeben, so dass ich beliebige Arrayinhalte in meiner Main bearbeiten könnte. Das gelingt mir leider nicht. Gibt es da eine Möglichkeit oder bin ich auf dem Holzweg?
Danke.
Gruß.
ottto
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 11.12.14 17:17 
ein simples
ausblenden C#-Quelltext
1:
public string[] Lesen()					


funktioniert nicht ?

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

Für diesen Beitrag haben gedankt: ottto
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 11.12.14 17:17 
Ja, das geht ganz einfach. Ändere den Rückgabetyp auf string[] und gib direkt arrDateiInhalt zurück.

Für diesen Beitrag haben gedankt: ottto
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 11.12.14 17:18 
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
public string[] Lesen()
{
    .....
    return arrDateiInhalt;
}



Zum Stil. Ich würde den jeweiligen Pfad der Lesen/Schreiben Methode übergeben und nicht als Properties veröffentliche. Das "_neu" dranfummeln ist dann auch eher was für den Aufrufer der Klasse. Ein belibieger Nutzer deiner Klasse möchte ja nicht unbedingt da "_neu" stehen haben. Als Properties eignet sich eher sowas wie der Separator (das Semikolon). Im Konstruktor kannst du ja für diese Property erstmal das Semikolon setzen damit man die nur benutzen muß wenn man was anderes als das Semikolon will.

Edit: Haben deine Dateien nur eine Datenzeile? Dein String.Split auf den gesamten Dateiinhalt wird nicht richtig mit Zeilenumbrüchen umgehen können. Bei mehreren Datenzeilen hättest du dann auch eher ein 2-D Array (string[][]).

Für diesen Beitrag haben gedankt: ottto
ottto Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 20



BeitragVerfasst: Do 11.12.14 17:40 
Da hab ich wohl den Wald vor lauter Bäumen nicht gesehen. -> Funktioniert.

@Ralf: Die Dateien haben nur eine Zeile. Deine Anregungen werde ich noch einarbeiten.

Vielen Dank für die schnelle Hilfe!!!
Gruß.
ottto