C# Amons C# Übungsthema

Amon

assimiliert
Liebe Boardies,

wie Ihr vielleicht noch nicht alle erfahren habt, bin ich seit dem ersten April glücklicher (und gestresster) Angestellter. Ich habe einen Arbeitsplatz gefunden, der meiner Ausbildung zum Fachinformatiker für Systemintegration entspricht und gleichzeitig aber auch Programmierkenntnisse in C# von mir fordert, die ich mir nun am aneignen bin. :)

Ich habe bereits per PN bei Palladin007 um Unterstützung gebeten, da er ja hier im Forum als C# Experte und militanter Anghänger der .NET Umgebung (scnr :p) bekannt ist.
Tut mir leid @Palladin007, dass ich nicht so zeitnah auf deine PNs antworten kann, bin im Moment echt ordentlich ausgelastet, mit Familie (Kind), Beruf und Gemeinschaftsprojekt.

Da Palladin007 mich gebeten hat ihm auch den Quellcode meiner bisherigen Übungsprogrämmchen zu schicken, dachte ich mir, ich mach das lieber hier öffentlich als per PN, dann können auch andere C# Hilfesuchende davon profitieren oder Experten sich mit ihrem Wissen einbringen.

Soviel erstmal zur Eröffnung, ich denke ich werde den Thread so gliedern, dass ich jedem Projekt einen eigenen Beitrag widme, auf den dann gerne mit Kommentaren zum Code, Verbesserungs- und Erweiterungsvorschlägen, Fehlerkorrekturen usw. geantwortet werden darf.

Liebe Grüße
Euer Amon
:)
 
Zuletzt bearbeitet:
Mein erstes Übungsprojekt, die Aufgabe habe ich von meinem Chef bekommen. Es geht darum, Dateien in einem Ordner hierarchisch in Unterordner zu sortieren nach dem Aufbau [Erstellungsdatum] [Jahr]-->[Monat]-->[Tag]. So habe ich es gelöst (// Der Code ist etwas übertrieben ausführlich kommentiert, zu meinem eigenen Verständnis):
PHP:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;



namespace SortFiles
{
    class Program
    {
        public static void Main()
        {
            // Pfad des Ordner mit den zu sortierenden Dateien angeben 
            Console.WriteLine("Bitte Verzeichnispfad angeben: \n");          
            string path1 = Console.ReadLine();
            // Abfrage rekursiv j/n
            Console.WriteLine("Unterverzeichnisse mit einbeziehen?" + Environment.NewLine + "Ja oder Nein? ");
            // Die in der Konsole eingegebene Taste wird in der Variable "eingabekey" gespeichert
            ConsoleKeyInfo eingabekey = Console.ReadKey();
            // Die Klasse clFiles wird instanziert
            Files clf = new Files();
            
            // Abhängig von der Nutzereingabe werden wird nur der Verzeichnisinhalt des Hauptordners oder 
            // inkl. Unterverzeichnisse eingelesen           
            if (eingabekey.Key == ConsoleKey.J)
            {
                clf.Files = Directory.GetFiles(path1, "*", SearchOption.AllDirectories);
            }
            else if (eingabekey.Key == ConsoleKey.N)
            {
                clf.Files = Directory.GetFiles(path1, "*", SearchOption.TopDirectoryOnly);
            }

            // In der Schleife werden wird jeder Dateiname aus clf.Files (stringarray "Files") durchlaufen            
            foreach (string filename in clf.Files)
            {
                // Für die Datei wird ein Objekt der Klasse "FileInfo" erstellt
                FileInfo fi = new FileInfo(filename);                

                Console.WriteLine("Erstellungsdatum:" + fi.CreationTime);
                /* In der Variablen "cty" wird das Erstellungsjahr der Datei gespeichert; 
                 * auf diese Information kann über das Objekt "fi" der Klasse "FileInfo" zugegriffen werden
                 * Dasselbe für Monat und Tag des Erstellungsdatums
                 */
                 
                string cty = fi.CreationTime.Year.ToString();
                string ctm = String.Format("{0}", fi.CreationTime.ToString("MMMM"));
                string ctd = fi.CreationTime.Day.ToString();

                /* Der Pfad zum Hauptordner wird mit dem Erstellungsjahr/-monat/-tag kombiniert und den entsprechenden 
                 * Variablen zugewiesen
                 */
                string ctyPath = System.IO.Path.Combine(path1, cty);
                string ctmPath = System.IO.Path.Combine(ctyPath, ctm);
                string ctdPath = System.IO.Path.Combine(ctmPath, ctd);

                //Mit der Pfadinfo aus den Variablen werden die entsprechenden Unterordner erstellt
                System.IO.Directory.CreateDirectory(ctyPath);
                System.IO.Directory.CreateDirectory(ctmPath);
                System.IO.Directory.CreateDirectory(ctdPath);

                // Die jeweilige Datei wird in den passenden Unterordner verschoben
                fi.MoveTo(ctdPath + @"\" + fi.Name);

                Console.WriteLine("Verschiebe Dateien: \n", ctdPath);
            }                 


            Console.ReadKey();
            
        }
    }
}

Und hier die dazugehörige Klasse Files, die habe ich in erster Linie erstellt um auf die Dateinamen global zugreifen zu können:
PHP:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SortFiles
{
    public class Files
    {
        private String[] files;
        public String[] Files
        {
            get { return files; }
            set { files = value; }            
        }
            
    }
}
 

Anhänge

  • SortFiles.rar
    198,3 KB · Aufrufe: 158
Zuletzt bearbeitet:
Kannst du vielleicht den ganzen Projekt-Ordner als rar verpackt hoch laden, das macht das eigene durchlesen, testen, kompilieren und debuggen deutlich einfacher.


Zu dem Übungsprojekt von deinem Chef:
Die Klasse Files kannst du auch weg lassen und gleich ein String-Array cerwenden. ^^
Oder du verwendest eine Liste, die im Prinzip genauso funktioniert, nur noch ein paar kleine Erleichterungen beinhaltet, wie z.B. die Add-Methode:
PHP:
using System.Collections.Generic;

class Program
{
    public static void Main
    {
        List<string> files = new List<string>();
        files.Add(@"Datei\Pfad");
    }
}


Das restliche Projekt hab ich mir noch nicht angeschaut, mache ich aber gleich jetzt.


So, jetzt zum restlichen Projekt:

Erst mal vorneweg: Viele Kommentare ist überhaupt nicht negativ, sondern eher positiv, da sie (wenn sie bei entsprechender farblicher Gestaltung, z.B. im Visual Studio) leicht zu überlesen oder ausblenden sind und daher nicht stören. Wer nun aber einen Teil des Codes nicht auf Anhieb versteht, kann dann auf diese Kommentare zurück greifen.
In gewisser Weise gehört das auch dazu, einen Quelltext ausführlich zu kommentieren, wenn man ihn an Dritte weiter gibt.

Zum Code, da werde ich mir einfach einzelne Dinge raus picken und wenn möglich mit dem Code-Abschnitt einleiten.

PHP:
Console.WriteLine("Unterverzeichnisse mit einbeziehen?" + Environment.NewLine + "Ja oder Nein? ");
Die Tatsache, dass du Environment.NewLine verwendest, ist in dem Sinne positiv, da so sicher gestellt ist, dass die Zeichenfolge, die eine neue Zeile darstellt, unabhängig vom System immer richtig gewählt wird. Das machen viele nicht (ich auch nicht), hauptsächlich, da die meiste Software für Windows selber geschrieben wird und da sieht diese Zeichenfolge so aus: "\r\n"

PHP:
            if (eingabekey.Key == ConsoleKey.J)
            {
                clf.Files = Directory.GetFiles(path1, "*", SearchOption.AllDirectories);
            }
            else if (eingabekey.Key == ConsoleKey.N)
            {
                clf.Files = Directory.GetFiles(path1, "*", SearchOption.TopDirectoryOnly);
            }
Vergiss die letzte else-Anweisung nicht, denn sonst wird dieser Teil bei falscher Eingabe einfach übersprungen und es gibt einen Fehler, da die Datei-Liste nicht vorhanden ist. In so einen Fall bietet sich meiner Meinung nach am besten an, eine Fehlermeldung zu geben, die Konsole zu leeren und mittels goto an eine vorherige Stelle zu springen. Irgendwie hat sich bei mir der Gedanke eingeschlichen, das sei schlechter Programmierstil, ob das so stimmt, weiß ich nicht, aber bei kleinen Anwendungen und in Fällen wie diesen kann man sich das durchaus mal erlauben. Ansonsten ist eine do-while-Schleife besser geeignet, da die erste Abfrage ohne Überprüfung durchgeführt wird und das Wiederholen der Abfrage bei ungültiger Eingabe direkt gewährleistet ist.

PHP:
string ctyPath = System.IO.Path.Combine(path1, cty);
/* ... */
System.IO.Directory.CreateDirectory(ctyPath);
/* ... */
Du verwendest überall die volle Definition der Namensräume, das ist gar nicht nötig und sollte gelassen werden, da sich ein Quelltext dann leichter lesen lässt. Visual Studio kann diese using-Direktiven, die dann notwendig wären, auch autoatisch dazu schreiben, schau dazu einfach mal im Kontext-Menü unter "using-Direktiven sortieren" (so hieß es glaube, kann mich aber irren :D).
Außerdem kannst du mit using eine Direktive in eine Variable speichern, fals es zu Namens-Konflikten kommen könnte:
PHP:
using Dateiverarbeitung = System.IO;

class Program
{
    static void Main()
    {
        string ctyPath = Dateiverarbeitung .Path.Combine(path1, cty);
    }
}



Zusammen fassend kann man wahrscheinlich sagen, es ist ein gelungenes kleines Projekt. Es ist auch deutlich übersichtlicher und in mancher Hinsicht sauberer, als ich das gerne mache, daher bleib gleich bei dem Stil, denn wer erst einmal seinen eigenen Stil hat, will den nicht mehr wechseln. :D
Die wenigen Punkte, die ich genannt habe, sind (bis auf einen) auch gar nicht wild. Nur die unnötige Klasse Files vergrößert die Projektmappe unnötig, bringt in meinen Augen aber keinen Vorteil.
Was da aber problematischer ist, das ist der else-Block, den du vergessen hast, denn der lässt Fehler offen zu und das darf nicht sein. So ein Programm muss immer idiotensicher funktionieren können, dass du möglichst viele Fehler möglichst früh schon in der Programm-Logik selber abfängst oder gleich unmöglich machst. Man kann sie zwar auch anders behandeln (try-catch-Anweisung), doch das sollte meiner Meinung nach nur dann gemacht werden, wenn ein anderer Weg gar nicht oder nur mit größen Umständen möglich ist.
 
Zuletzt bearbeitet:
Hallo Ammon,

wenn das dein erstes Projekt'chen' ist, dann schon mal Respekt zur Lösung.

Meine 2 Cent dazu:

Ohne auf stilistische Aspekte einzugehen, würde ich dir raten, sämtliche Eingaben vor der Weiterverarbeitung zu prüfen (EVA).
Du sparst dir viel Kummer, denn nicht jeder Tester ist ein goodwill - Kandidat und es soll auch DAU's geben...
Außerdem spart es dem Anwender Frust, wenn er zeitnah mitgeteilt bekommt was er evtl. falsch eingegeben hat.

Wie verhält sich CreateDirectory wenn ein bereits vorhandenes Verzeichnis nochmals angelegt werden soll?

Code:
// Die in der Konsole eingegebene Taste wird in der Variable "eingabekey" gespeichert
            ConsoleKeyInfo eingabekey = Console.ReadKey();
Wenn die Projekte wachsen sind sprechende Variablennamen Gold wert. Eine Variable, die die Information "Unterverzeichnisse einbeziehen" enthält, könnte z.B. auch
Code:
UnterverzeichnisseEinbeziehen
heißen.


BTW:
ja/nein Abfrage [Archiv] - Multimediaxis ???
 
ALso die Ja-Nein-Abfrage würde ich ja persönlich schon so auf rollen, wie er das gemacht hat, also dass es drei Ausgaben gibt:
Eine für "Ja", eine für "Nein" und eine Dritte für jede andere falsche Eingabe.

Ob das nun aber unter Stil gekehrt werden kann, weiß ich nicht.
 
Naja die von ElSer verlinkte Ja/Nein Abfrage ist Quick&Dirty würde ich mal sagen. Belastet den Anwender nicht unnötig mit Fehlermeldungen, ist aber auch nicht 100% korrekt alles andere als ja, als nein zu werten.
 
Das mein ich ^^

Aber Fehlermeldungen finde ich schon in Ordnung, wenn der User schlicht zu doof ist, entweder J oder N zu drücken, dann ist es seine Schuld, wenn es einen Fehler gibt. :D
An sich gebe ich lieber eine Meldung aus, als dem User irgendeine Entscheidung voraus zu greifen, die er dann vielleicht gar nicht so wollte.
 
Zuerst mal Vielen Dank an Amon für diesen Thread, ich will mich nämlich auch in diese Sprache einarbeiten.(y)

Den Aussagen von Palladin und Elser kann ich voll zustimmen möchte aber noch etwas anfügen:

- Wir sind ja nicht nur Programm-Ersteller sondern öfter auch -nutzer, darum mache ich das, was mich an anderen Programmen stört selber besser...

- Ich mag aussagekräftige Fehlermeldungen, die mich schnell zur Quelle des Problems führen.

- Ein Programm muss damit umgehen können, wenn ich mal was falsches eingebe.

Ich finde du machst beim Programmieren schon vieles richtig. Eine klare Gliederung und Einrücken erleichtern das spätere Anpassen erheblich.
Kommentare mache ich meist oberhalb eines Blockes, um dessen Zweck zu beschreiben. Verwende sprechende Variablennamen und niemals
Nummerierungen im Namen. (zB TitelErsterAbschnitt anstelle von Titel1).

Ach und besonders wichtig: Verwende niemals (NIEEEE-MALS) goto. :eek:
 
Nagut, dann ist der Gedanke, goto ist schlechter Programmierstil, ja wohl stimmig. :D

Ich hab es deshalb selber noch nie verwendet, da wähle ich lieber den Weg über eine Schleife, auch wenn der goto-Weg zuerst einfacher klingt, aber rein aus der Vollständigkeit halber sollte man es kennen, schließlich gehört es zur Sprache. ^^
 
@duffguy: Und sage niemals niemals ;)

goto ist ein legitimes Mittel zur Ablaufsteuerung, in Ungnade gefallen durch ausgiebige und undurchschaubare Nutzung. Sinnvoll eingesetzt mag es durchaus seine Berechtigung haben (s.z.B. goto (C#)) Notwendig ist es m.E. nach nicht (ich habe es seit BASIC V2 Zeiten nicht mehr eingesetzt) .
Schlecht durchschaubarer Code läßt sich auch ohne goto erreichen (z.B. try-catch-finally in einer rekursiv aufgerufenen Methode)
 
MeinJaNur... Ich habe goto das letzte mal bei meinen Basic Gehversuchen anno 1815 verwendet (damals noch mit Zeilennummer, GOTO 140)

Wenn du da mal einen semantischen Fehler suchen darfst, lernst du Spaghetti-Code hassen. Dieses Kommando zur Ablaufsteuerung sollte wenn
überhaupt, nur in homöopathischer Dosierung benutzt werden.

Ich stelle mir gerade vor, rekursiv durch einen Tree zu wandern, um dann bei einem Trigger irgendwo hinspringen :))
 
Naja, ich denke mal, wie sind uns einig ^^

Der Weg, goto zu verwenden, ist vielleicht nicht die feine englische Art und kann schnell zu Problemen und schwer zu findenden Fehlern führen. Dennoch ist es ein Teil der Sprache C#. Wer damit viel zu tun ha und damit vielleicht sogar seine Brötchen verdient oder verdienen will, der sollte das Sohlüsselwort, sowie seine Bedeutung dennoch kennen. Irgendwann findet sich bestimmt etwas, wo es von Nutzen sein kann und wenn es in einer Switch-Anweisung ist.
 
Oben