Autor Beitrag
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 13:11 
ausblenden C#-Quelltext
1:
class Datenklasse					


Du hast keine Sichtbarkeit angegeben. Ohne explizite Angabe ist die Sichtbarkeit erstmal internal und somit nur von innerhalb der gleichen Assembly nutzbar. Der Serializer kommt aus dem Framework und gehört nicht zu deiner Assembly sondern halt zu den Framework Assemblies. Für denn ist Datenklasse nicht erreichbar. Datenklasse sollte public sein (nicht nur wegen dem Serializer). Du solltest dir auch angewöhnen immer die Sichtbarkeit anzugeben auch wenn es einen default gibt.
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 13:29 
Dankeschön :) der Fehler ist nun behoben.
Aber leider Gottes kommt jetzt ein Fehler beim Speichern :autsch: :flehan:

Bei dem Speicherncode bei typeof(IO) kommt nun der Fehler: Fehler beim Reflektieren des Typs 'Geldverwaltung.IO'.

Ich habe es auch schon statt IO mir Datenklassen und Datenklasse probiert. Nichts funktioniert :(

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 13:36 
Zitat:
XmlSerializer ser = new XmlSerializer(typeof(IO));
ser.Serialize(fs, Datenklassen);


Was für ein Typ ist Datenklassen? List<Datenklasse>! und was hast du gesagt möchtest du serialisieren IO! Wer ist IO wenn du in einer IO Klasse bist. this!
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: Mi 06.01.16 13:57 
Ich will ja nicht verwirren, aber ich fände es besser (wie schon geschrieben) List<Datenklasse> zu serialisieren und nicht die Hilfsklasse IO (ich finde die beiden Namen immer noch sehr unglücklich gewählt!).

Edit: Mit wenig Änderung an der Klasse IO könnte man daraus dann nämlich eine generische Klasse machen, welche jeden beliebigen Typ serialisieren kann ;-)


Zuletzt bearbeitet von Th69 am Mi 06.01.16 14:02, insgesamt 1-mal bearbeitet
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 13:58 
Entschuldigung. Ich stelle mich gerade echt dumm an aber ich habe jetzt folgendes gemacht:
ausblenden C#-Quelltext
1:
2:
XmlSerializer ser = new XmlSerializer(typeof(IO));
ser.Serialize(fs, this);

Jetzt kann das Objekt nicht reflektiert werden :motz: :angel:

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 14:37 
Das ist jetzt so richtig.

Was jetzt noch fehlt ist tricky und kann man fast nicht wissen sondern muß es schmerzlich lernen ;) Du hast der Klasse einen Konstruktor gegeben. Damit hat die keinen automatischen Standardkonstruktor mehr denn bekommt man nur wenn man sonst keinen Konstruktor hat. Ein new Datenklassen() geht also nicht mehr. Wie erzeugt der XmlSerializer die Klasse er erzeugt die über den Standardkonstruktor und befüllt danach dessen Properties. Datenklasse hat aber keinen ;) Gib der Datenklasse also einen Standardkonstruktor (einen ohne Parameter) oder entferne den mit Parameterm um den automatischen Standardkonstruktor zu bekommen.

Typischer Fall von ist alles ganz simpel .... wenn man es schon weiß ;)


Zitat:
Edit: Mit wenig Änderung an der Klasse IO könnte man daraus dann nämlich eine generische Klasse machen, welche jeden beliebigen Typ serialisieren kann ;-)


Darum hab ich ihm geraten eine Klasse zu machen und die nicht von der Liste abzuleiten sondern diese als Property hinzuzufügen damit er die später um weitere Properties erweitern kann. Das in seiner Anwendung nur die Datenklasse(n) zu speichern sind ist ja eher unwahrscheinlich ;) Da jetzt noch um IO eine generischen Serializer-kapsel drumzulegen um laden/speichern rauszuziehen wäre ein Boni für später. Und ja die Namen sind nicht nur unglücklich sondern scheiXXXe. Aber so wie er entwickelt hat "ich packe das was ich auf dem Bildschirm sehe in eine Klasse" verhindert die gute Benamsung der Klassen. Wenn wir irgendwas übliches aus der Problemwelt nehmen würden um sie als Bezeichner der Klassen zu nehmen (Einnahmen/Ausnahmen/Saldo/Buchungen oder sowas) würde das nicht passen und wäre noch problematischer als schlechte generische Namen. Seine Klassen bilden einfach noch kein benennbares Problem ab. Für später sollte man das redesignen zum Beispiel die Datenklasse durch ein Buchungsklasse ersetzen. Dazu müßte man aber dann auch z.B. bemerken das AlterBetrag/NeuerBetrag keine tatsächliche Eigenschaften der Klasse(n) sind sondern berechnete Größen aus der Menge der Buchungen.
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 14:50 
Ich habe jetzt den Konstruktor der Klasse entfernt. Aber new List<Datenklasse>(); fällt ja jetzt weg. Daher kann ich dieser Liste nichts mehr zufügen. Aber jetzt kommt der Fehler: der Objektverweis wurde nicht auf eine Objektinstanz festegelegt.

Und wenn ich trotzdem new List<Datenklasse>(); schreibe, kommt der Fehler mit dem Reflektieren auf :crying: :crying: :crying: :crying: :crying:

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mi 06.01.16 15:02 
Nicht der Konstruktor von IO muss entfernt werden, der hat keine Parameter, an denen sich der Serializer stören würde.
Es geht um Datenklasse, woher soll der Serializer denn wissen, welche Parameter der Konstruktor bekommt?

Das erklärt auch das Verhalten. Die Liste, die nie zugewiesen wurde, wird vorher verwendet, er kommt also nie zum speichern.
Schreibst Du den Konstruktor, wo die Liste gesetzt wird, wieder rein, dann kommt dein Programm dort weiter und schafft es bis zum Speichern, wo der alte Fehler noch wartet.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 15:09 
keine Ahnung was dein problem ist.

Ich habe deinen Code jetzt mal genommen (+ die besprochenen Änderungen) und das geht.
Laden war noch Buggy, du hast geprüfst mit Directory.Exists ob ein File existiert und ich habe zusätzlich in speichern noch ein Directory.CreateDirectory(s) ergänzt.


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:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace ConsoleApplication35
{
    class Program
    {
        static void Main(string[] args)
        {
            Datenklasse daten = new Datenklasse() { Datum = DateTime.Now.ToString(), Art = "Hallo", Beschreibung = "Welt", AlterBetrag = 0.0m, Betrag = 5.5m, NeuerBetrag = 5.5m };
            IO funktionen = new IO();
            funktionen.Datenklassen.Add(daten);
            funktionen.Speichern("Datei.xml");

            IO funktionen2 = IO.Laden("Datei.xml");
        }
    }

    public class Datenklasse
    {        
        public string Datum { get; set; }
        public string Art { get; set; }
        public string Beschreibung { get; set; }
        public decimal AlterBetrag { get; set; }
        public decimal Betrag { get; set; }
        public decimal NeuerBetrag { get; set; }
    }

    public class IO
    {
        public List<Datenklasse> Datenklassen { get; set; }
        public IO()
        {
            Datenklassen = new List<Datenklasse>();
        }
        public static IO Laden(string FileName)
        {
            string s = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Finanzverwaltung");
            string path = Path.Combine(s, FileName);
            if (File.Exists(path))
            {
                using (FileStream stream = new FileStream(path, FileMode.Open))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(IO));
                    return (IO)xml.Deserialize(stream);
                }
            }
            else
            {
                return null;
            }
        }

        public void Speichern(string FileName)
        {
            string s = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Finanzverwaltung");
            Directory.CreateDirectory(s);
            string path = Path.Combine(s, FileName);

            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                XmlSerializer ser = new XmlSerializer(typeof(IO));
                ser.Serialize(fs, this);
            }
        }

        public static string defaultPath
        {
            get
            {
                return (Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Finanzverwaltung"));
            }
        }
    }
}
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 15:25 
Ja. Jetzt haut es hin. Ich wusste nicht, dass man den Feldern (strings) auch ohne Konstruktor sofort einen Wert zuweisen muss. Jetzt tüftle ich mir nur noch einen Algorithmus zusammen, der den Datein noch entsprechende Nummern gibt und dann sollte alles hin hauen. Aber es gibt jetzt nicht wirklich einen großen Unterschiedn zwischen dem von mir vorher benutzten StreamWriter, oder doch?

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
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: Mi 06.01.16 15:53 
Csharp-programmierer hat folgendes geschrieben:
Ja. Jetzt haut es hin. Ich wusste nicht, dass man den Feldern (strings) auch ohne Konstruktor sofort einen Wert zuweisen muss.

Nicht "muss", sondern "kann"!

Edit: was mir vorhin schon aufgefallen ist: der dreifache exakt gleiche Code zum Pfadzusammensetzen
Besser:
ausblenden C#-Quelltext
1:
string s = DefaultPath; // und Eigenschaftennamen mit großem Anfangsbuchstaben					
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 16:22 
Definiere Unterschied. Wenn du die Software nie nie wieder änderst weil du dir nur jetzt verwendest und in Zukunft nie wieder ist es immer egal wie du es tust. Wenn es jetzt funzt egal wie ist das Problem gelöst. Software lebt aber üblicherweise. Jemand muss die erweitern ändern etc. Das passiert auch üblicherweise nicht zeitnah.

Stell dir vor in 2 Jahren mußt du Datenklasse um eine Property erweitern. Das du die UI passend ändern mußt wird offensichtlich sein. Das du in Laden/Speichern was ändern mußt nicht. Ein simpler Streamwriter Code wird nicht beim kompilieren meckern aber die zusätzliche Property auch nicht speichern. Die Wahrscheinlichkeit ist hoch das du das einfach vergisst. Die jetzige Lösung wird das einfach automatisch mit erledigen. Der Code ist also deutlich Wartungssicherer. Das ist eigentlich immer der primäre Grund für einen bestimmten Codestil. Ist das pflegbar auch von jemanden anderen als ich selbst. Kann ich selbst das noch pflegen auch wenn ich mittlerweile vergessen habe wie es funktioniert.

Darum reiten wir ja hier auch z.B. so auf dem Thema Naming rum gutes Naming macht Software wartbar. Und richtige Namen lassen einen auch richtig über das Problem nachdenken. Sowohl beim Programmieren als auch beim warten der Software.
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Mi 06.01.16 16:30 
Hallo,

ich habe jetzt mal ein komplettes Beispielprojekt angehängt (mit meinem Schüler und Schulklasse Beispiel). So würde ich sowas angehen.

In der Anwendung kannst du Schüler in einer LisstView hinzufügen oder entfernen. Mit "Save" wird das ganze in ner XML Datei gespeichert (die liegt im Debug Ordner des Projekts) und mit Load entsprechend wieder geladen.

@Ralf
Apropos Naming: "ConsoleApplication35" ;)
Einloggen, um Attachments anzusehen!
_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 16:37 
Schönes Beispiel den XmlWriter in der Serializer Klasse solltest du noch einen using verpassen damit auch Dispose aufgerufen wird. Sonst bleibt das File dahinter eine unbestimmte Zeit gesperrt.
Der AddForm würde ich die Student Klasse übergeben und an die Controls binden anstatt die Einzelproperties.

Für diesen Beitrag haben gedankt: C#
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 19:02 
Also das Speichern klappt jetzt. Aber jetzt fehlt noch das Öffnen. Ich deserialisier ja IO aber wie kommt die Datenklasse dort mit rein?

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 19:10 
Zitat:
Ich deserialisier ja IO aber wie kommt die Datenklasse dort mit rein?


IO ist die Summe seiner Properties also wird die List<Datenklasse> auch mit serialisiert sie ist Teil von IO. Du mußt dich nicht fragen wie du noch die Datenklasse serialiseren/deserialisieren mußt. Es passiert bereits.
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Mi 06.01.16 19:54 
Achso ja stimmt. Nur mal eine Frage: alle Felder mit { get; set; } werden serialisiert / deserialisiert, oder? Also ohne getter und setter nicht?

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:
if (sender != null)
            {
                ToolStripDropDownItem d = (sender as ToolStripDropDownItem);
                string s = Path.Combine(IO.defaultPath, d.Text, "Finanzen");
                HauptPfad = Path.Combine(IO.defaultPath, d.Text);
                string[] files = Directory.GetFiles(s);

                foreach (string file in files)
                {
                    IO io = IO.Laden(file);
                    foreach (var objekt in io.Datenklassen)
                    {
                        Datenklasse daten = objekt;
                        ListViewItem i = new ListViewItem(daten.Datum);
                        i.SubItems.Add(daten.AlterBetrag.ToString());
                        i.SubItems.Add(daten.Art);
                        i.SubItems.Add(daten.Betrag.ToString());
                        i.SubItems.Add(daten.NeuerBetrag.ToString());
                        i.SubItems.Add(daten.Beschreibung);
                        this.listView1.Items.Add(i);
                    }
                }
            }


Ich habe in meinem Toolstrip ein Item, über welches man die einzelnen Geldanlagen öffnen kann. Die Items dafür werden dynamisch erzeugt. Jetzt tritt in der foreach- Schleife bei io.Datenklassen der Fehler aus: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.

Aber ich habe ja darauf hingewiesen? Woran liegt das jetzt schon wieder? :think:

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 06.01.16 20:55 
Ich habe so langsam das Gefühl wir/ich helfen völlig an dir vorbei. Möglicherweise solltest du noch mal ein Schritt zurückmachen und kleiner Brötchen backen :cry:

Was soll in dem Code das iterieren über files? Es sollte mittlerweile klar sein das alle Daten in genau einer IO Instanz stecken. Es gibt nur ein einziges File das du lesen solltest. Ich kann dir nicht sagen was da jetzt dieses konkrete Problem auslöst. Spekulativ liest du alle möglichen Files in dem Ordner die da noch in irgendeinem Zustand zwischenzeitlicher Versuche rumliegen und irgendwas bei dir auslösen. Das wäre hier die richtige Stelle um mal deine Debugging Fähigkeiten zu trainieren. Also Debug den Code, gehe ihn per Debugger Schritt für Schritt durch. Schau dir dabei an ob in den einzelnen Variablen das steht was du erwartest und mach dir dann Gedanken wenn irgendwas nicht deinen Erwartungen entspricht.
Csharp-programmierer Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 696
Erhaltene Danke: 10

Windows 8.1
C# (VS 2013)
BeitragVerfasst: Do 07.01.16 21:10 
Hallo Ralf.

Ja. Ich habe gestern nur sehr wenig Zeit gehabt aber wollte versuchen das Problem mit dem Serialisieren schnell zu lösen. Ich habe nun den von Ihnen geschriebenen Code abkopiert und es haut hin. Ich weiß zwas im Moment noch nicht den Unterschied zwischen meinem :gruebel:

Ich versuche in den nächsten Tagen den Code zu analysieren und zu verstehen. Ich melde mich dann nochmal.
Und noch mal einen großen Dank für Ihre Motivation und Ausdauer mir bei all meinen Problemen geholfen zu haben. Echt tolles Forum :zustimm:

_________________
"Wer keinen Sinn im Leben sieht, ist nicht nur unglücklich, sondern kaum lebensfähig" - Albert Einstein
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Do 07.01.16 21:26 
Zitat:
Und noch mal einen großen Dank für Ihre Motivation und Ausdauer mir bei all meinen Problemen geholfen zu haben. Echt tolles Forum :zustimm:


Gern geschehen. Falls ich hier und da mal genervt geklungen haben sollte. Sorry.