Zum Inhalt springen

Whiz-zarD

Mitglieder
  • Gesamte Inhalte

    2076
  • Benutzer seit

  • Letzter Besuch

  • Tagessiege

    50

Alle Inhalte von Whiz-zarD

  1. Und wer bezahlt die Wohnung? Mit Sicherheit nicht der Azubi selbst, wenn man bedenkt, dass ein Azubi oft nicht mal volljährig ist, wenn er die Ausbildung anfängt ... Ich nehme nur mal Raum Hamburg, wo die Kalt-Miete einer Ein-Zimmer-Wohnung schon das durchschnittliche Einkommens eines Azubis übersteigen kann. Von der Kaution will ich gar nicht sprechen. Da bleibt nichts anderes übrig, als in Hotel Mama weiterzuwohnen oder die Eltern bezahlen die Wohnung. Mag sein, dass es Gegenden gibt, wo sich Azubis eine Wohnung leisten können, aber dies ist bei weitem nicht überall so. Selbst Studium ist nicht gleich Studium. Ich kenne inzwischen einige Bachelor-Absolventen. Sowohl von Fachhochschulen als auch von Universitäten und irgendwie ist jeder der Meinung, dass Fachhochschulen praxisorientierter aber stressiger sind, als Universitäten und das deckt sich auch mit meinen Erfahrungen, da ich in meiner Assistenten-Ausbildung die meist die Kurse und Vorlesung, wie auch die FH-Studenten, die im selben Hause studierten, besuchte und sehr häufig musste ich auch noch die Nacht durcharbeiten, weil am nächsten Morgen um 8 Uhr der Abgabetermin der Übungen war. Zu meiner Zeit als Azubi kam sowas nie vor.
  2. Ich würde eigentlich sogar noch einen Schritt weitergehen: Weg von Powerpoint! Ich finde, es gibt nichts langweiligeres als Powerpoint-Präsentationen. Gerade ITler wollen nicht mit Zahlen und Fakten gelangweilt werden, sondern die wollen etwas sehen. Ich würde es langweilig finden, wenn jemand ein Vortrag über ein Stück Code hält und dabei nur ein Folien zeigt, anstatt die IDE, womit man den den Code direkt "anfassen" und ausprobieren kann. Außerdem bin ich der Meinung, dass Powerpoint nur etwas für Leute ist, die technisch keine Ahnung haben. Ich bin ein Freund von HTML-Präsentationen. Das läuft in jedem Browser und man braucht keinen unsinnigen Powerpoint-Viewer. Die Zuschauer brauchen auch nur den Link und können sich noch mal die Präsentation anschauen. Da schmeiße ich mal reveal.js in den Raum. Das ist ein Framework für schicke HTML-Präsentationen. Der Vorteil ist auch, dass man hier nur mit reinen Text arbeitet und mit Hilfe einer Quellcodeverwaltung Änderungen an der Präsentation leicht nachvollziehen lassen können. So habe ich schon einige Vorträge auf Konferenzen gesehen, die gleich auf der Bühne korrigiert und online gestellt worden sind. Für dieses Framework gibt es auch einen visuellen Editor. Wenn man also gerade in der Abschlusspräsentation beweisen möchte, dass man was drauf hat, würde ich von Powerpoint Abstand halten und schauen, ob es da nicht bessere Alternativen gibt und auch wenn man sich gegen Powerpoint entscheiden sollte, sollte man gerade bei Präsentationen, die hauptsächlich ITlern ansprechen sollte, mit Folien sparsam sein. Ob man jetzt Witze einbaut, sollte jeder für sich selbst entscheiden. Es gibt Leute, die bringen Witze sehr gut rüber und können die Präsentation ein wenig auflockern. So gibt es bei mir in der Firma jemanden, der selbst langweilige und trockene Präsentationen zu Themen, wie z.B. Marktanalysen, sehr unterhaltsam präsentieren kann. Andere können es wiederum nicht und bei solchen Leuten wirken Witze wie erzwungen.
  3. Die Frage ist, wie sollen wir dir nun helfen? Ich kann dir die erste Frage in deinem ersten Post beantworten. Die Frage ist dann aber, ob du das überhaupt verstehst. Und das führt mich zu folgendes: Ich behaupte mal frech: Nein, du kannst es nicht. Zumindest noch nicht. Programmieren ist mehr, als nur ein paar Zeilen Code zu schreiben, die man nicht versteht. Das du den Code selber nicht verstehst, sieht man daran, dass du Kommentare brauchst, um den Code zu erklären, damit du noch am nächsten Tag weißt, was der überhaupt macht. Ich sehe hier zwei große Probleme, die du erstmal in den Griff bekommen müsstest, um weiterkommen zu können: Du musst die Objektorientierung verstehen Du musst das Konzept bzw. die Syntax von C# verstehen Dir ist offenbar nicht klar, dass Klassen aus Methoden und Eigenschaften bestehen. Zugriffsmodifizierer In C# kann man mit Hilfe der Zugriffsmodifizierer die Sichtbarkeit der Methoden und Eigenschaften bestimmen: public: Die Methode/Eigenschaft ist von außen sichtbar private: Die Methode/Eigenschaft steht nur der direkten Klasse zur Verfügung; sie ist von außen nicht sichtbar protected: Die Methode/Eigeschaft steht der direkten und vererbten Klasse zur Verfügung; sie ist von außen nicht sichtbar internal: Die Methode/Eigenschaft steht nur im eigenen Projekt zur Verfügung. internal ist mehr ein Sonderling und kommt in der Praxis relativ selten vor. Es wird oft verwendet, wenn man private Klassen in einem Unittest testen möchte, da man die Möglichkeit besitzt, den Zugriff auf solche Klassen dennoch in anderen Projekten zu erlauben. Dies muss man aber explizit in der Projekt-Datei angeben. Eigenschaften Eigenschaften lassen sich über get abrufen und über set setzen. Eigenschaften definiert man wie folgt: class A { public int Nummer { get; set; } } Bei dieser Art der Implementierung einer Eigenschaft spricht man auch von einer "automatisch implementierten Eigenschaft", da der C#-Kompiler dahergeht und Standard-Getter- und Setter-Methoden generiert, da der getter- und setter nicht ausprogrammiert worden sind. Der Kompiler generiert daraus folgenden Code: class A { private int nummer; public int get_Nummer() { return this.nummer; } public void set_Nummer(int value) { this.nummer = value; } } Wie man sehen kann, wird dann ein privates Feld und eine öffentliche get- und set-Methode generiert, mit denen man das Feld von außen steuern kann. Natürlich kann man auch hier die Sichtbarkeit einschränken. Beispiel: class A { public int Nummer { get; private set; } } Der Getter ist dann öffentlich (public) und der Setter ist nicht öffentlich (private). Natürlich kann man auch das public gegen einen anderen Modifizierer ersetzen: class A { protected int Nummer { get; set; } } In diesem Beispiel ist der Getter- und Setter protected. Also nur in der direkten und vererbten Klasse sichtbar und von außen nicht. Man kann auch selbst den Getter- und Setter implementieren, wenn man möchte, dass noch zusätzlich was passiert. Häufig ist dies der Fall, wenn man ein Event auslösen möchte, wenn eine Eigenschaft sich ändert. Dann muss man auf die automatische Implementierung der Standard Getter- und Setter-Methoden verzichten und es selbst schreiben: Beispiel: public class A { private int nummer; public int Nummer { get { return this.nummer; } set { Console.WriteLine("Nummer gesetzt auf {0}", value) this.nummer = value; } } } In diesem Beispiel wird auf der Konsole der neue Wert für Nummer ausgegeben. In der Set-Methode steht die Variable value zur Verfügung, die den neuen Wert besitzt. Der Kompiler würde daraus folgenden Code erzeugen: class A { private int nummer; public int get_Nummer() { return this.nummer; } public void set_Nummer(int value) { Console.WriteLine("Nummer gesetzt auf {0}", value) this.nummer = value; } } Die Schreibweise mit get {...} und set {...} ist also nichts weiter als syntaktischer Zucker, um den Code kompakter zu machen. Daraus werden dann auch nur Methoden generiert. Methoden Methoden können einen Rückgabewert besitzen oder nicht. Wie man schon beim generierten Getter- und Setter-Methoden der Eigenschaften sehen kann, gibt ein void an, dass die Methode kein Rückgabewert besitzt. Ersetzt man void hingegen gegen einen Datentyp, so gibt die Methode einen Wert vom genannten Datentyp zurück. Beispiel: public string GetHallo() { return "Hallo"; } Die Methode GetHallo() gibt den String "Hallo" zurück. Zu deiner ursprünglichen zweiten Frage: Ich habe mir mal den Code vom StreamReader angeschaut. Die Dispose()-Methode ruft Close() auf. Sprich, wenn du um den StreamReader ein using machst, wird automatisch Close() aufgerufen, wenn Dispose() aufgerufen wird. Hier mache ich aber auch erst mal schluss, da ich nicht weiß, ob du überhaupt verstehst, was ich hier geschrieben habe. Wenn du es verstanden haben solltest, kann ich noch was zu Klassen und Generics schreiben.
  4. Sorry, aber ich kann nichts dafür, wenn du mit dem großen 1x1 anfangen willst, auch wenn du das kleine nicht kannst. Dann muss ich sagen, dann ist das Projekt noch zu groß für dich. Dann fange mit kleineren Dingen an. Bei deinem Projekt brauchst du schon etwas mehr Übung und Erfahrung, um es wirklich auf die Beine stemmen zu können. Du hast vor, mehrere komplexe Themen zu bearbeiten und du verstehst nicht mal, wie man das erste Thema löst. Dann kaufe ein Buch für Anfänger und arbeite es durch. So wird das einfach nichts.
  5. Ich verstehe deine Nummerierung nicht. Zuerst gibst du an, dass die CSV-Datei so aufgebaut ist: Datum, Packstück-Nummer, Auftrags-Nummer, Paketnummer, Bezeichnung, Menge, Lagerplatz Dann sagst du, dass du die siebte Nummer braucht und das wäre nach meiner Zählung "Lagerplatz". Wo kommt denn deine "7. Nummer her"? Von was?
  6. Das virtual könnte man auch weglassen. Ich schreibe es nur aus Gewohnheit hin, weil das NHibernate-Framework dies so möchte. Außerdem bietet es mir beim Testen gewisse Vorteile. virtual bedeutet, dass diese Eigenschaft mittels Vererbung überschreibbar ist. Für gewöhnlich sind Eigenschaften und Methoden in C# nicht überschreibbar. Wenn man in einer vererbten Klasse eine Eigenschaft oder Methode aber überschreiben möchte, so muss sie in der Basis-Klasse als virtual definiert werden. Beispiel: public class A { public virtual string Hallo { get { return "Hallo"; } } } public class B : A { public override string Hallo { get { return base.Hallo + " Welt"; } } } Die Eigenschaft Hallo in Klasse A liefert "Hallo" zurück, während die Klasse B "Hallo Welt" zurückliefert. Die Klasse B überschreibt die Eigenschaft von Klasse A und ruft mit base.Hallo erst mal die Eigenschaft in Klasse A auf und modifiziert diesen Wert. Was ist denn die "5. Nummer jeder Zeile"? Welche Spaltenüberschrift besitzt dann diese Zeile?
  7. Ich nehme mal an, dass das aus der Logistik kommt. Ich kenne mich da mit den Begrifflichkeiten nicht aus. Ich nehme aber mal an, dass Daten aus deiner Firma sind. Die Daten stammen ja mit Sicherheit auch aus einer Datenbank, also muss das Kind bei euch ja einen Namen haben. Frage doch deinen Ausbilder. Spontan und ohne den Kontext zu kennen, würde ich aber behaupten, dass es sich hierbei um eine Kanban-Karte für einen Logistiker handelt, der mit diesen Informationen die Pakete packt. Unabhängig von dem Klassennamen kannst du aber dennoch schon den Inhalt der Klasse füllen: public class Xyz { public virtual DateTime Datum { get; set; } public virtual int PackstueckNummer { get; set; } public virtual int Auftragsnummer { get; set; } public virtual int Paketnummer { get; set; } public virtual string Bezeichnung { get; set; } public virtual int Menge { get; set; } public virtual string Lagerplatz { get; set; } } Für Xyz muss dann noch ein passender Name gefunden werden. Bei PackstueckNummer, Auftragsnummer, Paketnummer und Lagerplatz musst du dann mal schauen, ob die Datentypen richtig sind. Ich kenne die Daten nicht. Kann auch sein, dass Buchstaben erlaubt sind, dann müssen es Strings sein.
  8. Einfacher geht es kaum noch. Ich versuche es aber mal. Du hast eine For-Schleife. Im Kopf stehen folgende Informationen: Die Zählervariable heißt i => int i = 0 i wird mit 0 initialisiert => int i = 0 Die Schleife läuft so lange, wie i kleiner 5 ist => i < 5 Pro Schleifendurchlauf wird i um 1 erhöht => i++ Das bedeutet, du hast 5 Schleifendurchläufe bei jedem Schleifendurchgang wird folgende Logik angewendet: if (i == 3) { result += 10; } else { result += i; } D.h. wenn i gleich 3 ist, dann wird auf result 10 addiert. Ansonaten i. Jetzt gehen wir die Durchläufe Schritt-für-Schritt durch: Durchlauf: i = 0, result = 0 i ist nicht 3, also wird auf result 0 addiert. result ist 0; also: result = result + i = 0 + 0 = 0 Durchlauf: i = 1, result = 0 i ist nicht 3, also wird auf result 1 addiert. result ist 0; also: result = result + i = 0 + 1 = 1 Durchlauf: i = 2, result = 1 i ist nicht 3, also wird auf result 2 addiert. result ist 1; also: result = result + i = 1 + 2 = 3 Durchlauf: i = 3, result = 3 i ist 3, also wird auf result 10 addiert. result ist 3; also: result = result + 10 = 3 + 10 = 13 Durchlauf: i = 4, result = 13 i ist nicht 3, also wird auf result 4 addiert. result ist 13; also: result = result + i = 13 +4 = 17 Beim 6. Durchlauf hätte i den Wert 5 und somit entspricht i nicht mehr der Bedingung, dass es kleiner als 5 ist und bricht die Schleife ab. result ist dann 17 und wird in der Konsole ausgegeben.
  9. Und wo genau liegt das Problem? Gehen wir mal die Schleife durch: i result ------------------------------------ 0 result += 0 => result = result + 0 => result = 0 + 0 = 0 1 result += 1 => result = result + 1 => result = 0 + 1 = 1 2 result += 2 => result = result + 2 => result = 1 + 2 = 3 3 result += 10 => result = result + 10 => result = 3 + 10 = 13 4 result += 4 => result = result + 4 => result = 13 + 4 = 17 Auf result wird pro Schleifendurchlauf immer i dazu addiert. Die Ausnahme ist, wenn der Zähler i bei 3 ist. Dann wird 10 draufaddiert. Der += operator ist nur eine Abkürzung result += i ist das selbe wie result = result + i
  10. Der Code ist nicht vollständig. Der Kopf der For-Schleife ist nicht vollständig. Und was machst du gerade? Bis jetzt sehe ich nur, dass wenn der Zähler i gleich 3 ist, auf das Ergebnis 10 addiert wird, ansonsten 1.
  11. Web-Technologie und C# schließen sich nicht aus. Eine moderne Web-Applikation besteht aus Microservices und wie die Microservices entwickelt wurden, ist dem Browser schnuppe. Das kann jetzt C#, F#, Java, Python, C, C++, Go, Fortran, R oder was auch immer sein. Die Web-Applikation kommuniziert mit dem Server über eine Web-API und holt sich dort die Daten ab. Das HTML-Gerüst wird dann im Browser mittels JavaScript-Frameworks wie z.B. AngularJS oder React clientseitig gerendert. Daher trennt man zwischen Frontend- und Backend-Entwickler. Frontend-Entwickler machen den clientseitigen JavaScript-Kram, während der Backend-Entwickler sich auf dem Server austobt. AngularJS benutzt als Sprache auch TypeScript, was Ähnlichkeiten mit C# aufweist und auch von Microsoft ist. Gerade Microservices bieten die Möglichkeit, sehr flexibel mit der Programmiersprache zu sein. Ist es besser, eine Aufgabe mit F# zu entwickeln, dann nimmt man halt F#. Für eine andere Aufgabe ist C# vielleicht besser. Man legt sich hier also nicht auf eine Sprache fest, sondern man wählt die geeignete Sprache. Das selbe gilt auch für die Datenhaltung. Bei sog. Monolithen ist es oft der Fall, dass man sich für eine relationale Datenbank entscheidet aber vielleicht ist so eine Datenbank für bestimmte Aufgaben nicht flexibel genug. Für bestimmte Microservices nimmt man dann vielleicht eine NoSQL-Datenbank oder vielleicht sogar einen ganzen Hadoop-Cluster, während andere wiederum weiterhin mit einer relationalen Datenbank arbeiten. Was Mobile angeht, sollte man sich nicht fragen, wo dort C# steht, denn C# hat seinen Platz eher im Backend, sondern die Frage, ob es noch Sinn macht, native Apps zu entwickeln? Bei Spielen macht es vielleicht noch Sinn aber bei Apps, die auch als Web-Applikation laufen können? Inzwischen können auch Browser auf die Sensoren zugreifen und auswerten. Der Bedarf an nativen Apps sinkt also meiner Meinung nach. Abgesehen davon gibt es auch noch im Frontend-Bereich Xamarin, womit man Android-, und iOS-Apps nativ mit C# entwickeln kann. Für die iOS-Kompilierung ist aber ein Mac von nöten.
  12. Eine CSV-Datei ist ja auch nichts weiteres, als eine Tabelle. Was für Daten stecken denn in der Datei? Ob die Daten doppelt und dreifach sind, ist ja auch erstmal egal. Ein Problem nach dem anderen.
  13. Suche unter Google nach "coding katas" und versuche sie zu bearbeiten. Eine Liste findest du z.B. hier
  14. Mein Beispiel ist bei weitem nicht perfekt. Ich wollte dich auch nicht entmutigen, sondern einfach zeigen, dass auch erfahrene Entwickler erstmal anfangen und dann den Code stückweise verbessern. Niemand schreibt auf Anhieb guten Code. Man muss aber Erfahrungen sammeln, um ein Gefühl für die Sprache zu bekommen. Mit Erfahrungen merkt man auch, wenn etwas vielleicht nicht ganz so elegant ist und man vielleicht noch mal drüber nachdenken sollte. Ich wollte nur aufzeigen, wie ich an solche Dinge rangehe. Du solltest einfach Mut besitzen, mal etwas auszuprobieren. Kaputtmachen kannst du ja nichts. Wenn du sogar eine quellcodeverwaltung, wie z.B. Git, nutzt, dann kannst du sogar ganze Änderungen wieder rückgängig machen. Du kannst auch Fragen stellen und mit anderen Leuten über dein Code reden. Ich denke, niemand reißt dir deswegen den Kopf ab. Das einzige, was ich erwarte, ist, dass man auch die Ratschläge von anderen annimmt und sie mal ausprobiert.
  15. Um Dinge zu lernen, geht es viel weniger um tolle Projekte, sondern um Techniken zu lernen: O/R-Mapper (Entity Framework, NHibernate) IoC/DI container (z.B. StructureMap, AutoFac) Logging (z.B. SeriLog, NLog) Message Queues (z.B. RabbitMQ) Unittests Microservices Web-APIs OData Linq Oder du optimierst dein Programmierstil. Lerne Clean Code und TDD (Test-driven Development) und verinnerliche die SOLID-Prinzipien. Selbst wenn du eine WinForms-Anwendung gebaut hast, könntest du mit Sicherheit diese noch weiter optimieren. z.B. kannst du versuchen, diese mit dem MVP-Pattern (Model-View-Presenter) umzubauen oder du verwendest das Event-Aggregator-Pattern um die Kommunikation zwischen den einzelnen Forms zu realisieren. Eigentlich gibt es immer was zu optimieren und kann dabei neue und nützliche Dinge lernen. Ich finde, man muss diese Techniken nicht alle bis zur Perfektion verinnerlicht haben. Ich finde, man sollte aber mal davon gehört und ggf. auch ein mal ausprobiert haben. Die Vertiefung einzelner Techniken kommt hinterher, wenn man ein Projekt gefunden hat, was man umsetzen möchte.
  16. Ich habe eine Assistenten-Ausbildung gemacht aber auf einer Schule die, zumindest in Raum Hamburg und Schleswig-Holstein, einen sehr guten Ruf genießt und ich denke auch, dass die Fachinformatiker-Ausbildung noch sehr viel Verbesserungspotenzial besitzt aber es kommt immer darauf an, was man später daraus macht. Ein Studium macht dich auch nicht zu einem Super-ITler, der ein Allround-Genie ist. Zuerst solltest du dir mal überlegen, was du eigentlich möchtest? Möchtest du eher Software entwickeln oder lieber die IT-Infrastruktur am Leben erhalten (ich nenne es einfach mal so)? Beides sind doch sehr unterschiedliche Berufsfelder, auch wenn sie einen gewissen Schnittpunkt haben. Ein FISI wird z.B. wenig mit Java arbeiten und ein FIAE wenig Netzwerke- und Server administrieren. Dann kommt es darauf an, was du aus einem Abschluss machst. Selbst mit einem Studium-Abschluss kannst zu einem Entwickler versauern, der nach kurzer Zeit zu nichts zu gebrauchen ist, wenn man nicht bereit ist, sich weiterzuentwickeln. Falls du dich mehr für Softwareentwicklung begeisterst, brauchst du eigentlich auch weniger Zertifikate. Bei einem Entwickler kommt es eigentlich mehr auf die Projekte an, an denen er mitgewirkt hat und ehrlich gesagt, wüsste ich auch nicht, welche Zertifikate man da überhaupt machen könnte. Ob man die 2.000 € Netto erreicht, hängt auch von der Branche ab, wo man arbeiten möchte. Aus Erfahrung weiß ich, dass man in der Spielebranche kaum die 2.000 € erreichen wird. Dort ist die Bezahlung eher mies. In der Finanz- oder Versicherungsbranche sieht die Welt auch schon wieder ganz anders aus. Die Qualifikation ist also nicht immer entscheidend. Ich arbeite in der Finanzbranche und ich habe dein Wunschgehalt schon in wenigen Jahren erreicht. Auch ohne Zertifikate oder Studium.
  17. Wieso sollte man dich töten wollen? Softwareentwicklung ist nun mal ein Reifeprozess. Niemand liest nur ein Buch und kann gleich wunderbar sauberen Code schreiben. Mein Code sah zum Anfang auch mies aus und selbst Robert C. Martin, der das Buch "Clean Code" geschrieben hat, sagt von sich aus, dass er nicht die Weisheit mit Löffeln gefressen hat und es auch bei seinen Code-Beispielen sicherlich noch Verbesserungspotenzial gibt aber nur durch Ausprobieren lernt man. Du hast schon richtig erkannt, dass man fürs Einlesen der Datei eine eigene Klasse benötigt. Allerdings gehört die Logik nicht in den Konstruktor. Der Konstruktor dient zur Initialisierung der Klasse. Der Name der Klasse sollte auch die Aufgabe widerspiegeln, was die Klasse tut. "DateiEinlesen" ist vielleicht gut, aber geht es vielleicht noch konkreter? Ich weiß, dass es eine CSV-Datei ist. Vielleicht eher CsvReader? Wobei dieser Name auch wieder sehr allgemein ist. In der CSV-Datei steckt ja eine Tabelle. Welche Daten besitzt die Tabelle? Vielleicht kann man der Tabelle einen Namen geben. Eine CSV-Datei ist ja eine Art der Serialisierung. Das Verfahrung um so eine Tabelle in ein Objekt zu überführen, nennt man auch Deserialiserung. Das kann man ja erst mal im Hinterkopf behalten. Zuerst würde ich mir aber erst mal eine geeignete Datenstruktur überlegen. In der CSV-Datei stecken ja Daten. Ich nehme jetzt mal als Beispiel, dass die CSV-Datei Daten zu Personen beinhaltet: Name;Vorname;Geschlecht;Alter Doe;John;Maennlich;38 Also würde ich erst mal eine Klasse für diese Daten erstellen: public class Person { public string Name { get; set; } public string Vorname { get; set; } public Geschlecht Geschlecht { get; set; } public int Alter { get; set; } } public enum Geschlecht { Maennlich , Weiblich } Nun könnte ich mich darum kümmern, eine(!) Datei einzulesen. Ich habe eine Datenstruktur und ich weiß, dass ich eine CSV-Datei deserialisieren muss. Also könnte man die Klasse z.B. PersonCsvDeserializer nennen. In dieser Klasse soll es eine Methode geben, die Deserialize() heißt. Ich verzichte hier jetzt erst mal bewusst auf ein Interface, weil ich denke, dass es für dich bis hier hin schon kompliziert genug ist. Das Interface werde ich später noch mal erklären. Erst mal kümmern wir uns darum, was wir alles brauchen, um eine Datei zu deserialisieren. Was muss die Klasse PersonCsvDeserializer alles wissen, um eine CSV-Datei deserialisieren zu können? Man könnte vielleicht im ersten Schritt auf die Idee kommen, dass die Klasse den Pfad und Dateinamen benötigt. Mit den Informationen aus dem letzten Absatz könnte ein erster Entwurf so aussehen: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { // ... } } Als Rückgabewert habe ich IEnumerable<Person> gewählt, weil IEnumerable<T> ein sehr allgemeines Interface ist und einen Enumerator (auf deutsch: Aufzählung; in anderen Sprachen auch Iterator genannt) zur Verfügung stellt, mit dem wir über die Daten iterieren können (mit der foreach-Schleife). Sowohl IList<T>, ICollection<T>, IDictionary<T>, Array und weitere Klassen implementieren dieses Interface und mehr als über die Daten iterieren wollen wir nicht. Wenn wir später damit mehr machen wollen, können wir es leicht mit Linq in eine Collection, List, Array oder auch in ein Dictionary umwandeln. Die Deserialize()-Methode soll also eine Aufzählung von Personen zurückliefern. Normalerweise macht man es anders, aber aus einfachheit behaupte ich mal frech, dass die erste Zeile in der CSV-Datei immer ein Header besitzt. In der Implementierung überspringe ich den Header per Linq mit der Skip()-Methode. Die Deserialize()-Methode soll also folgendes machen: Die Datei lesen Durch die Datenzeilen iterieren Pro Datenzeile ein Person-Objekt erstellen Die Person-Objekte als Aufzählung zurückliefern Der erste Entwurf könnte daher folgendermaßen aussehen: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { IList<Person> result = new List<Person>(); foreach (string line in File.ReadAllLines(fileName).Skip(1)) { string[] elements = line.Split(';'); result.Add(new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }); } return result; } } Die Methode macht zwar was sie soll, aber ist sie wirklich übersichtlich? Nicht wirklich. Wir haben hier mehrere Ebenen miteinander vermischt. Wir können also mit dem Refactoring anfangen. z.B. das File.ReadAllLines(fileName).Skip(1) Wofür ist das genau gut? Wenn man den gesamten Kontext kennt, weiß man es zwar aber eigentlich liegt der Code-Abschnitt eine Ebene Tiefer. Es hantiert mit Dateien und hat mit der eigentlichen Aufgabe der Deserialiserung wenig zu tun. Also sollte man diesen Teil in eine separate Methode packen: private IEnumerable<string> ReadDataFromFile(string fileName) { return File.ReadAllLines(fileName).Skip(1); } Somit wandert das Skip(1) in eine tiefere Ebene und interessiert uns in der Deserialize()-Methode nicht mehr. Als nächstes fällt aber auf, dass wir ein String mit Split() in ein Array teilen und aus diesem Array dann die einzelnen Personendaten herausfischen. Diesen Vorgang nennt man auch Parsing. Also könnten wir diesen Teil auch in eine Methode auslagern: private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } Unsere Klasse sieht dann bis jetzt folgendermaßen aus: public class PersonCsvDeserializer { public IEnumerable<Person> Deserialize(string fileName) { IList<Person> result = new List<Person>(); foreach (string serializedData in ReadDataFromFile(fileName)) { Person person = this.Parse(serializedData); result.Add(person); } return result; } private IEnumerable<string> ReadDataFromFile(string fileName) { return File.ReadAllLines(fileName).Skip(1); } private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } } Nun ist Deserialize() doch recht gut lesbar. Wir lesen die Daten aus der Datei, parsen die Daten und erhalten ein Person-Objekt, welches wir dann in eine Liste packen und zum Schluss geben wir die Liste zurück. Es gäbe hier noch weiteres Verbesserungspotenzial aber ich belasse es erst mal hierbei. Ein paar Hinweise gebe ich aber noch: Fehler-Handling? Was passiert, wenn z.B. die Datei nicht existiert? Ist das erzeugte Objekt List<Person> wirklich eine gute Wahl? Angenommen, wir haben es mit einer riesigen CSV-Datei (mehrere Gigabytes) zu tun, die größer ist, als unser Arbeitsspeicher. Hier schmeiße ich mal das "yield return"-Schlüsselwort in den Raum. Auch ist das indexierte Zugreifen auf das Array in der Methode Parse() nicht wirklich glücklich gelöst. Was passiert nämlich, wenn mal eine Spalte in der Datei hinzukommt? Dann muss man ja auch den Code anpassen. Das will man aber eigentlich gar nicht. Zu diskutieren wäre auch, ob die Variable fileName nicht doch besser eine Instanzvariable sein sollte, die per Konstruktor reingereicht wird. Es fällt ja auf, dass die Methoden Deserialize() und ReadDataFromFile() den Dateinamen benötigen. Also stellt fileName ja eine gewisse Abhängigkeit dar, die die Klasse benötigt, um arbeiten zu können. Als Überlegung kannst du ja selber mal schauen, wie man mit solchen Situation umgehst. Um später im Hauptpgramm alle Personen zu iterieren könntest du nun folgendes schreiben: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); PersonCsvDeserializer deserializer = new PersonCsvDeserializer(); foreach(string fileName in fileNames) { IEnumerable<Person> persons = deserializer.Deserialize(fileName); foreach (Person person in persons) { // ... } } } Nach dem selben Prinzip, wie bei der PersonCsvDeserializer-Klasse kannst du ja mal überlegen, wie man nun diesen Code refactoren an. Ab hier wird es noch etwas technischer und tiefgreifender. Ich möchte dir noch zwei Techniken zeigen, die du aber erst mal nicht umsetzen brauchst. "Inversion of Control" und "Dependeny Injection" In der Klasse PersonCsvDeserializer fällt auf, dass die Klasse von einer Datei abhängig ist aber die Daten können vielleicht aus einer Datenbank kommen oder wir schreiben die CSV-Daten direkt in eine grafische Oberfläche. Möchte man jetzt für jeden Anwendungsfall eine eigene Klasse schreiben? Eigentlich nicht. Die Abhängigkeit zur Datei muss also aufgelöst werden. Das .Net-Framework bietet ja die abstrakte Klasse TextReader, die so ziemlich alles darstellen kann. Ein Reader, der eine Datei liest oder aus einem TCP-Stream oder aus einer Datenbank, etc. Anstatt also den Dateinamen reinzureichen, könnte man auch ein TextReader reinreichen. Hier mal ein Beispiel, wie so eine Klasse aussehen könnte: public class PersonCsvDeserializer { private TextReader reader; private bool isHeaderSkipped; public PersonCsvDeserializer(TextReader reader) { this.reader = reader; } public IEnumerable<Person> Deserialize() { string serializedData; while ((serializedData = this.ReadNextData()) != null) { Person person = this.Parse(serializedData); yield return person; } } private string ReadNextData() { string serializedData = this.reader.ReadLine(); if (!this.isHeaderSkipped) { this.isHeaderSkipped = true; return this.ReadNextData(); } return serializedData; } private Person Parse(string serializedData) { string[] elements = serializedData.Split(';'); return new Person { Name = elements[0], Vorname = elements[1], Geschlecht = (Geschlecht)Enum.Parse(typeof(Geschlecht), elements[2]), Alter = Convert.ToInt32(elements[3]) }; } } Die Main-Methode sieht dann so aus: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); foreach(string fileName in fileNames) { using (TextReader reader = File.OpenText(fileName)) { PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); IEnumerable<Person> persons = deserializer.Deserialize(); foreach (Person person in persons) { // ... } } } } Zugegeben, in diesem Beispiel ist die Klasse PersonCsvDeserializer etwas komplizierter geworden aber es ist jetzt egal, woher die Daten stammen, solange wir ein TextReader in den Konstruktor schieben. Das reinrechen der Abhängigkeit in den Konstruktor nennt sich auch "Dependeny Injection". In diesem Beispiel habe ich auch das yield return verwendet. Da wir jetzt nur noch maximal den Speicher für ein Person-Objekt verbrauchen, könnte die Klasse eigentlich nun unendlich viele Daten deserialisieren. Ein Problem stellt aber immer noch die Indexierung des Arrays dar aber das überlasse ich jetzt dir. Das Interface Das letzte, was ich noch schreiben wollte, wäre ein geeignetes Interface für den Deserializer. Wollen wir jetzt mehrere Deserializer schreiben oder einen Deserializer als Abhängigkeit in eine Klasse reinreichen, ist ein Interface geeignet, damit es später egal ist, um welchen Deserializer es sich handelt. Man könnte sich ja auch vorstellen, dass die Daten nicht in einer CSV-Datei stecken, sondern in einer XML-Datei. Dafür wäre folgendes Interface recht nützlich public interface IDeserializer<T> { IEnumerable<T> Deserialize(); } Mit diesem Interface könnten wir sogar das hässliche using im Hauptprogramm wieder loswerden. Ich finde, das using stört im Lesefluss. Wir haben ja jetzt eine Klasse, die CSV-Daten aus unterschiedlichsten Quellen von Personen deserialisieren kann. Was hindert uns nun daran, einen weiteren Deserializer zu bauen, der aus Dateien deserialisiert? Beispiel: public class PersonCsvFileDeserializer : IDeserializer<Person> { private string fileName; public PersonCsvFileDeserializer(string fileName) { this.fileName = fileName; } public IEnumerable<Person> Deserialize() { using (TextReader reader = File.OpenText(fileName)) { PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); return deserializer.Deserialize(); } } } Das using wurde nach PersonCsvFileDeserializer und somit eine ebene tiefer verschoben. Wenn du Dependecy Injection verstanden hast, dann würde dir auffallen, dass die Zeile PersonCsvDeserializer deserializer = new PersonCsvDeserializer(reader); eigentlich böse ist, da es eine Abhängigkeit darstellt, die wiederum in den Konstruktor gehört. Ich habe sie aber erst mal hier drinnengelassen, weil das sonst wieder bedeuten würde, dass das using wieder ins Hauptprogramm rein müsste. Eigentlich müsste man sich eine Fabrik-Methode ausdenken, die den PersonCsvFileDeserializer zusammenbaut. Die habe ich hier aber weggelassen. Die kannst du dir ja ausdenken. Das Hauptprogramm würde dann so aussehen: static void Main(string[] args) { string sourcePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\blabla"; IEnumerable<string> fileNames = Directory.GetFiles(rootPath, "*.csv"); foreach(string fileName in fileNames) { PersonCsvFileDeserializer deserializer = new PersonCsvFileDeserializer(fileName); IEnumerable<Person> persons = deserializer.Deserialize(); foreach (Person person in persons) { // ... } } } Das wäre doch schon wieder ein Schritt übersichtlicher. Wie du also siehst, haben wir allein nur für das Einlesen von den CSV-Dateien drei Klassen: Person PersonCsvDeserializer PersonCsvFileDeserializer und ein Interface: IDeserializer<T> geschrieben. Man braucht also kein mega großes Projekt, um mehrere Klassen zu schreiben. Es reicht auch schon was ganz einfaches. Man sollte sich immer bewusst machen, dass Klassen immer nur eine Aufgabe machen sollten und Methoden Teilaspekte dieser Aufgabe sind und sie sollten auch nicht mehr machen, als eine Sache. Es macht auch nichts, wenn man zum Anfang Spagetticode schreibt und diesen später nach und nach einem Refactoring unterzieht. Niemand ist perfekt und niemand schreibt perfekten Code. Man fängt also immer erst mal an und arbeitet sich Schritt für Schritt an eine geeignete und saubere Lösung. Selbst meine Lösung ist mit Sicherheit nicht perfekt und ich habe auch nicht die Weisheit mit Löffeln gefressen. Wenn du mein Beitrag richtig verfolgt haben solltest, hast du vielleicht auch gemerkt, dass ich erst mal eine Lösung geschrieben habe und sie dann nach und nach verfeinert und verbessert habe. Das Wissen kommt erst mit Erfahrung und Erfahrung sammelt man nur, indem man es ausprobiert und darüber mit anderen diskutiert. Also trau dich. So, das reicht auch fürs erste. Ich denke, das ist erst mal genug Input.
  18. Glaube mir mal, deine Probleme beruhen darauf, dass der Code so schlecht geschrieben wurde. Es ist äußerst schwierig, dort den Überblick zu behalten. Du sagst ja selber, dass du die Kommentare schreibst, um dich daran zu erinnern, was das soll aber so etwas brauchst du bei einem gut strukturierten Code nicht. Es ist ein trugschluss, wenn man meint, man spart Zeit, wenn man den Code hinfrickelt. Ja, mag sein, dass der initiale Aufwand geringer ist aber der Aufwand kommt bei der Fehlersuche und momentan raubt doch die Fehlersuche viel Zeit. Wenn dir das schon bei so einem kleinen Programm nicht der Sinn von Klassen erschließt, dann wohl gar nicht. Ich habe dir schon Beispiele genannt, wie man hier vier Klassen designen kann, wenn du das dann immer noch nicht verstehst, wann dann? Entweder du willst es lernen oder nicht. Wenn du es lernen willst, dann probiere es einfach aus. Versperr dich doch nicht.
  19. Bei einem using wird Dispose() immer am Ende des Blocks aufgerufen. Beispiel: Wir haben folgenden Code: try { using(TextWriter writer = new TextWriter(...)) { // Business-Logik } } catch(Exception e) { // Exception-Logik } Das wäre das selbe, wie das hier: try { TextWriter writer = new TextWriter(...); try { // Business-Logik } finally { if (writer != null) writer.Dispose(); } } catch(Exception e) { // Exception-Logik } Der Finally-Block wird immer aufgerufen. Komme was wolle. Es ist egal, ob innerhalb der Business-Logik eine Exception geworfen wird oder sie fehlerfrei durchläuft und genau so verhält sich auch das using. Es ruft immer am Ende des Blockes Dispose() auf. Du solltest aber wirklich erst mal die Grundlagen der Programmierung bzw. der Objektorientierung lernen. Ich sehe da einfach nur Schwarz. Du siehst ja selber, dass dein Code einfach unwartbar und unstrukturiert ist. Du solltest dir erst mal Gedanken machen, welche Aufgaben dein Programm erfüllen soll. Wenn ich den Code richtig verstehe, sehe ich da vier Aufgaben: CSV-Dateien einlesen Daten aus dem Internet herunterladen Daten parsen CSV-Dateien schreiben Also sehe ich hier schon mindestens vier Klassen. Du versuchst aber gleich mehrere Aufgaben in einer einzigen Methode zu erledigen und das kann einfach nicht gut ausgehen. Kommentare im Code sind auch fast immer ein Anzeichen dafür, dass der Code unsauber ist. Es gibt eigentlich nur sehr wenige Gründe, warum man ein Kommentar schreiben sollte.
  20. Das Close() sollte man noch zusätzlich aufrufen. Das schließt zwar die Datei aber gibt nicht die Ressourcen frei.
  21. Managed Code ist das, was von der Common Language Runtime (CLR) gemanaged wird. Die CLR ist die Laufzeitumgebung, in der dein C#-Code läuft. In der CLR läuft auch der Gargabe Collector, der eben den Speicher reserviert und wieder freigibt. Unmanaged Code ist das, was außerhalb der CLR läuft. Die Klasse TextWriter ruft also Funktionalitäten auf, die außerhalb der CLR liegen. z.B. wenn die Win32-API aufgerufen wird. Die Win32-API ist mit C++ geschrieben und direkt in Maschinencode kompiliert. Sprachen wie z.B. C/C++ besitzen keinen Gargabe Collector. Der Entwickler muss hier selbst für die Speicherreservierung- und Freigebung kümmern. Daher spricht man hier von "unmagaged Code", weil es keine automatische Unterstützung für diese Aufgabe gibt. Die Klasse TextWriter muss also selbst dafür sorgen, dass der Speicher der darunterliegenden Aufrufe bereinigt wird. Dafür wurde das IDisposable-Interface geschaffen, die die Dispose()-Methode besitzt und aufgerufen werden sollte, wenn man es mit solchen Klassen zu tun hat. Das using ist nur eine syntakische Erleichterung. Folgenden Code: using(TextWriter writer = new TextWriter(...)) { ... } könnte man auch folgendermaßen schreiben: TextWriter writer = new TextWriter(...) try { ... } finally { if (writer != null) writer.Dispose(); } Das using hat nämlich auch die Eigenschaft, dass das Dispose() immer aufgerufen wird, auch wenn eine Exception geworfen wird. Ein using musst du also so oder so verwenden. Es sei denn, du verwendest eine Bibliothek, um die CSV-Dateien zu erstellen, die das alles für dich abnimmt. Wie z.B. CsvHelper. Ob eine Klasse das IDisposable-Interface implementiert kann man aus der Dokumentation entnehmen oder man schaut sich die Definition der Klasse in Visual Studio an. Dafür klickst du einfach mit der rechten Maustaste auf den Klassennamen und wählst "Go To Definition" aus oder drückst F12.
  22. Der StreamReader und StreamWriter implementieren auch IDisposable. Bei solchen Klassen gilt besondere Vorsicht, da man sie im einem using benutzen sollte: using(StreamWriter writer = new StreamWriter(...)) { ... } Klassen, die IDisposable implementieren, besitzen sog. unmanaged code. D.h. sie werden vom Gargabe Collector nicht vollständig weggeräumt. Deswegen implementieren solche Klassen einen eigenen Destruktor, um die Ressourcen wieder freizugeben. Per using wird implizit die Dispose()-Methode aufgerufen, die dann die Ressourcen wieder freigibt. Ich vermute mal, dass dort dein Fehler liegt und du auf eine Ressource (sprich auf eine Datei) zugreifen möchtest, die auf der noch ein Handle liegt. Ich persönlich würde aber nicht so mit Dateien rumhantieren. Ich würde eine geeignete Datenstruktur überlegen und erst mal alles im Speicher machen und dann im letzten Schritt alles als eine Textdatei rauspusten.
  23. Die *.pdb-Datei erzeugt keine Datei. Sie dient lediglich dazu, mehr Debug-Informationen zu bekommen. Beispiel: static void Main(string[] args) { try { throw new NullReferenceException(); } catch (Exception e) { Console.WriteLine(e); } finally { Console.ReadKey(); } } Wenn ich jetzt das Programm ausführe, erhalte ich folgende Nachricht in der Konsole: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei ConsoleApplication3.Program.Main(String[] args) in d:\dokumente\visual studio 2015\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs:Zeile 16. Ich kann also sehen, dass die Exception in Zeile 16 in der Datei Program.cs geworfen wurde und die Zeile ist folgende: throw new NullReferenceException(); Auch sollte man die Meldung der Exception verstehen. Die obere Zeile ist die Art der Exception mit einer optionalen Nachricht. Der Teil: bei ConsoleApplication3.Program.Main(String[] args) Sagt, wo die Exception ausgelöst wurde und der Teil: in d:\dokumente\visual studio 2015\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs:Zeile 16. Ist der Stacktrace. Der Stacktrace kann noch deutlich länger werden, je nach dem wie viele Ebenen der Code absteigt und wieder raufklettern muss, um die Exception zu fangen. Wenn ich jetzt aber die *.pdb-Datei lösche und das Programm starte, erhalte ich nur folgende Meldung: System.NullReferenceException: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. bei ConsoleApplication3.Program.Main(String[] args) Ich sehe also nicht mehr, wo die Exception geworfen wurde, sondern lediglich nur noch die Methode, wo sie ausgelöst wird. Für dich ist also erst mal der Stacktrace wichtig, um zu wissen, wo überhaupt die Exception aufgetreten ist. Das spart auch schon vieles an Rätselraten. Wenn man die Stelle dann kennt, kann man sich überlegen, was eigentlich passiert ist. Ohne den Stacktrace zu kennen, wird das Debugging sehr schwer.
  24. Und trotzdem hast du nicht meine Frage beantwortet: Aus deinem Code entnehme ich dann, dass die Dateien nicht im Netzwerk-Ordner gelesen und gespeichert werden, sondern im Desktop-Ordner? Edit: Fürs lesen und Schreiben von CSV-Dateien gibt es auch fertige Bibliotheken. Da würde ich mir auch nicht mehr die Mühe machen und es selbst implementieren. Den CsvHelper finde ich persönlich nicht schlecht.
  25. Also willst du ein Webcrawler schreiben? Warum nimmst du denn nicht einen? z.B. HTTrack? Die IOException wird auch nicht vom WebClient ausgelöst. Laut Dokumentation werden folgende Exceptions geworfen: Exception Condition ArgumentNullException Der address-Parameter ist null. WebException Der URI-Format durch Kombination von BaseAddress und address ist ungültig. - oder - Fehler beim Herunterladen von Daten. NotSupportedException Die Methode wurde in mehreren Threads gleichzeitig aufgerufen. Dein Problem muss also beim Lesen oder Schreiben der Dateien zu tun haben. Da dieser Teil in deinem Code fehlt, kann ich dazu nichts sagen. Was genau sagt denn die Exception? Wie sieht denn der Stacktrace aus? Wenn die Symbol-Dateien (*.pdb) dabei liegen, bekommt man sogar die Zeilennummer mit, wo die Exception geworfen worde.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...