Verständnisfrage zu Try Catch



  • Hallo,

    ich versuche gerade testweise Daten aus einer Access DB einzulesen und weiterzubearbeiten.

    Dazu habe ich eine einfache Klasse geschrieben, die die Datenfelder aufnehmen soll (dbFields).

    Zu Beginn von Form1_load deklariere ich alle Variablen und auch ein Array über die Klasse. Im Konstruktor initialisiere ich das Array dann.

    array<dbFields ^> ^dbf = gcnew array<dbFields ^>(20);
    

    Dann gehe ich über Try Catch in die DB rein und schreibe die Ergebnisse in das Array der Klasse.

    Soweit funktioniert das auch.

    Nur wenn ich dann innerhalb einer anderen Funktion auf die Objekte zugreifen will, haben die Objekte keine Werte mehr. Gelten innerhalb von Try Catch zugiesene Variablen / Objekte als lokal?

    Wenn ja, wie könnte ich das so ändern, dass ich trotzdem auf die Objekte zugreifen kann?

    Noch ne Zusatzfrage: Da ich nicht weiss, wie groß das array werden soll, hatte ich überlegt, stattdessen mit vectoren zu arbeiten. Allerdings hatte ich hier die Meldung, das der Vector diesen Typ nicht verarbeiten kann. Ist das so?

    Danke & Gruß

    Solick



  • In try-catch-Blöcken gelten grundsätzlich dieselben Regeln wie in allen anderen Blöcken (z.B der Anweisung in einem if-Block); dein Fehler hat wohl nichts mit dem try-catch-Block zu tun. Zeig mal mehr Code, dann sieht man das Problem besser.

    solick schrieb:

    Noch ne Zusatzfrage: Da ich nicht weiss, wie groß das array werden soll, hatte ich überlegt, stattdessen mit vectoren zu arbeiten. Allerdings hatte ich hier die Meldung, das der Vector diesen Typ nicht verarbeiten kann. Ist das so?

    Die nativen STL Typen arbeiten nicht ohne weiteres mit .NET-Typen zusammen, es gibt jedoch unzählige andere Möglichkeiten. Da du mit .NET arbeitest, solltest du dessen Gegenstück System::Collections::Generic::List<typename T> benutzen. Wenn du sehr mit der STL-Syntax vertraut bist, dann kannst du den STL/CLR vector vector benutzen ( #include <cliext/vector> ); das sieht dann aus wie die native STL, es sind aber .NET-Typen.

    MfG



  • Hallo rant,

    danke für den Hinweis mit list... die hatte ich vollkommen vergessen 😃

    Zum ersten Teil meiner Frage hier ein bissl Code:

    Klasse dbFields:

    dbFields.h:

    #pragma once
    
    using namespace System;
    
    namespace DB_Functions {
    
    ref class dbFields
    {
    	String ^dbBundesland;
    	int dbRang, dbAktData;
    	float dbEinwohner;
    
    public:
    	dbFields(int rang, String ^bundesland, float einwohner, int zahl);
    
    	property int Rang
    	{
    		int get();
    		void set (int rang);
    
    	}
    
    	property float Einwohner
    	{
    		float get();
    		void set(float einwohner);
    
    	}
    
    	property String ^Bundesland
    	{
    		String ^get();
    		void set(String ^bundesland);
    
    	}
    
    	property int CountDB
    	{
    		int get();
    		void set(int zahl);
    	}
    
    };
    }
    

    dbFields.cpp:

    #include "StdAfx.h"
    #include "dbfields.h"
    
    using namespace System;
    
    namespace DB_Functions {
    
    	dbFields::dbFields(int rang, String ^bundesland, float einwohner, int zahl) : dbRang(rang), dbBundesland(bundesland), dbEinwohner(einwohner), dbAktData(zahl)
    	{
    
    	}
    
    	int dbFields::Rang::get()
    	{
    		return(dbRang);
    
    	}
    	void dbFields::Rang::set(int rang)
    	{
    		dbRang = rang;
    	}
    
    	float dbFields::Einwohner::get()
    	{
    		return(dbEinwohner);
    	}
    
    	void dbFields::Einwohner::set(float einwohner)
    	{
    		dbEinwohner = einwohner;
    	}
    
    	String ^dbFields::Bundesland::get()
    	{
    		return(dbBundesland);
    	}
    
    	void dbFields::Bundesland::set(String ^bundesland)
    	{
    		dbBundesland = bundesland;
    	}
    
    	int dbFields::CountDB::get()
    	{
    		return(dbAktData);
    	}
    
    	void dbFields::CountDB::set(int zahl)
    	{
    		dbAktData = zahl;
    	}
    
    }
    

    und Dann aus der Forms.h:

    Zuerst die Deklarationen (noch mit Array...)

    String ^verbindungsstr;
    		OleDbConnection ^verbindung;
    		OleDbCommand ^befehl;
    		OleDbDataReader ^leser;
    		int aktDatensatz, countmax;
    		array<dbFields ^> ^dbf;
    

    dann die Initialisierungen:

    verbindungsstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Users\\Matten\\Documents\\Visual Studio 2008\\DBs\\Laender.mdb";
    			verbindung = nullptr;
    			befehl = nullptr;
    			leser = nullptr;
    			array<dbFields ^> ^dbf = gcnew array<dbFields ^>(20);
    

    und dann die eigentliche Verbindungsherstellung uns das auslesen:

    try
    			{
    				//Verbindung herstellen
    				verbindung = gcnew OleDbConnection(verbindungsstr);
    				verbindung->Open();
    
    				//Daten aus der DB abfragen
    				befehl = verbindung->CreateCommand();
    				befehl->CommandText = "SELECT * FROM bndlaender";
    				leser = befehl->ExecuteReader();
    
    				//Daten in klassenarray kopieren
    				aktDatensatz = -1;
    				countmax = 0;
    
    				while(leser->Read())
    				{
    					countmax++;
    				}
    
    				//array<dbFields ^> ^dbf = gcnew array<dbFields ^>(countmax);
    
    				leser->Close();
    				leser = nullptr;
    				leser = befehl->ExecuteReader();
    
    				while(leser->Read())
    				{
    					aktDatensatz++;
    					dbf[aktDatensatz] = gcnew dbFields(leser->GetInt32(0), leser->GetString(1),leser->GetFloat(2), aktDatensatz);
    
    				}
    
    			}
    			catch(OleDbException ^e)
    			{
    				MessageBox::Show(e->Message, "Datenbankfehler");
    			}
    			catch(Exception ^e)
    			{
    				MessageBox::Show(e->Message, "Datenbankfehler");
    			}
    			finally
    			{
    				if (leser != nullptr)
    					leser->Close();
    				if(verbindung != nullptr)
    					verbindung->Close();
    			}
    
    			aktDatensatz = 0;
    
    			FillFields(aktDatensatz,countmax, dbf);
    

    Die Funktion FillFields:

    public: void FillFields(int zahl,int countmax, array< dbFields^> ^dbfarray)
    			{
    				if(zahl>-1 && zahl < countmax)
    				{
    					l_aktdata->Text = (zahl+1).ToString();
    
    					l_rang->Text = Convert::ToString(dbfarray[zahl]->Rang::get());
    					l_einwohner->Text = Convert::ToString(dbfarray[zahl]->Einwohner::get()) + " Mio.";
    					l_bundesland->Text = dbfarray[zahl]->Bundesland::get();
    				}
    
    			}
    

    Soweit läuft das Programm auch durch.

    Ich hab noch zwei Buttons zum vor und zurückschalten durch die Datensätze...

    wenn ich diese nun klicke, ruft der Ereignishandler die Funktion auf... genau dabei bekomme ich dann die Fehlermeldung...

    private: System::Void back_Click(System::Object^  sender, System::EventArgs^  e) 
    		 {
    			 FillFields(aktDatensatz, countmax, dbf);
    		 }
    

    Fehlermeldung: Der Objekthinweis wurde nicht auf eine Objektinstanz festgelegt...

    Hoffe, das hilft weiter und danke schon mal für die Hilfe..

    Gruß Solick



  • solick schrieb:

    dann die Initialisierungen:

    verbindungsstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Users\\Matten\\Documents\\Visual Studio 2008\\DBs\\Laender.mdb";
    			verbindung = nullptr;
    			befehl = nullptr;
    			leser = nullptr;
    			array<dbFields ^> ^dbf = gcnew array<dbFields ^>(20);
    

    Die letzte Zeile ist mir nicht ganz geheuer. Du verdeckst in diesem Scope die Membervariable dbf mit einem anderen Array dbf. Greifst du überhaupt auf die Membervariable dbf zu, wenn du initialisierst? Prüf das bitte mal 😉

    MfG



  • Sorry, versteh ich nicht.

    Ich deklariere doch oben das Array dbf über die Klasse dbfields und initialisiere dann konkret ein array dbf mit der Anzahl 20. Wo verdeckt denn da was?

    Wie gesagt, das Programm wird kompiliert und läuft durch. Nur wenn ich aus dem Button die Funktion aufrufe klappts nicht. Beim Debuggen habe ich gesehen, das die beiden Integer-Variablen die korrekten Werte haben, dbf aber nicht definiert ist, als ob die Werte gelöscht wurden.

    Das versteh ich ebend nicht, weil doch alle drei an den gleichen Stellen deklariert und initialisiert wurden...

    Gruß

    PS: List hab ich bisher nicht hinbekommen... da meckert der Compiler immer, dass er der Index nicht stimme...

    Beim CLI Vector will er mit dem Objekt nicht arbeiten...



  • solick schrieb:

    Ich deklariere doch oben das Array dbf über die Klasse dbfields und initialisiere dann konkret ein array dbf mit der Anzahl 20. Wo verdeckt denn da was?

    Nun, eigentlich solltest du aus deiner Beschreibung schon selbst hören, was das Problem sein könnte. Du initialisierst irgendein ein Array dbf, nicht aber dasjenige der Klasse dbfields, sondern ein neues, welches nach der Initialisierung (das ist in irgendeinem Konstruktor, right?) aber verloren geht 😉

    array<dbFields ^> ^dbf = gcnew array<dbFields ^>(20);
    

    Diese Zeile müsste doch wohl eher so aussehen:

    this->dbf = gcnew array<dbFields ^>(20); // this zur Verdeutlichung ;-)
    

    Die Fehlermeldung ( System::NullReferenceException ) würde zumindest genau darauf passen^^

    Wegen der List kannst du auch mal die Meldung vom Compiler posten 🙂

    MfG



  • Asche auf mein Haupt!!!

    Danke für den Hinweis, natürlich lag es daran... ich hatte in der Zeile das array noch einmal deklariert...

    So jetzt geht es mit arrays.

    Was ich noch nicht hinbekommen habe, ist das ganze mit Listen zu machen. Wenn ich die Arrays durch Listen ersetze, bekomme ich folgende Fehlermeldung:

    Der index Lag ausserhalb des Bereichs. Er muss nicht negativ und kleiner als die Auflistung sein. Parametername: Index.

    Das ganze wird bei folgender Zeile ausgelöst:

    dbf[aktDatensatz] = gcnew dbFields(leser->GetInt32(0), leser->GetString(1),leser->GetFloat(2), aktDatensatz);
    

    Gruß Solick



  • Habs hinbekommen...

    mit dbf->add() konnte ich das Objekt hinzufügen. Vielen Dank nochmal für die Hilfe!

    Gruß Solick



  • Noch ne Zusatzfrage:

    Ich habe gelesen, dass man auch direkt arrays / listen etc. in Klassen unterbringen kann und dann über einen Klassenindexer auf die einzelnen Elemente zugreift. In meinem Buch ist das aber nur so mit 3 Sätzen erwähnt ich werde daraus nicht schlau. Hast du mir einen Link wo ich das nochmal nachlesen kann oder einen kurzen Hinweis, wie man das macht?

    LG Solick



  • solick schrieb:

    Noch ne Zusatzfrage:

    Ich habe gelesen, dass man auch direkt arrays / listen etc. in Klassen unterbringen kann und dann über einen Klassenindexer auf die einzelnen Elemente zugreift. In meinem Buch ist das aber nur so mit 3 Sätzen erwähnt ich werde daraus nicht schlau. Hast du mir einen Link wo ich das nochmal nachlesen kann oder einen kurzen Hinweis, wie man das macht?

    LG Solick

    Direkt unterbringen? Das geht wohl kaum, sind doch die Dimensionen eines CLI-Arrays erst zur Laufzeit bekannt. Jedoch weiss ich was zum Klassenindexer, oder vielmehr über die Properties, welche es nach einem Indexer aussehen lassen:

    property T default[U]
    {
        T get(U);
        void set(U, T);
    }
    

    Definiere eine solche Eigenschaft für deine Klasse, und du kannst wie beim normalen C++-Subscriptoperator [] beliebige Operationen ausführen.

    MfG



  • Also meine Idee ist folgende:

    Im Sinne der Datenkapselung alles in eine Klasse unterzubringen:

    Einlesen der Daten im Konstruktor
    Verteilt auf die einzelnen Datenbankfelder, die als Eigenschaften und List-Elemente definiert sind.

    Dann entsprechende Methoden zum speichern, updaten etc...

    was genau würde denn die von Dir gepostete Indexdefinition adressieren? Ein Array über die Klasse, die Eigenschaften oder was?

    Grüße Solick



  • Hmm okay 🤡

    Nur damit wir uns richtig verstehen: willst du über eine Art Subscriptoperator auf alle möglichen Daten in deiner Klasse zugreifen, oder redest du von etwas völlig anderem? Ich verstehe deinen letzten Beitrag irgendwie nicht so recht 😕

    MfG



  • Also von der Idee her so:

    public class dbKlasse {

    List<int> Rang;
    List<String ^> ^Bundesland;
    List<float> Einwohner;

    dbKlasse(Rang, Bundesland, Einwohner)
    {
    // hier dann die Werte aus der Datenbank auslesen und nacheinander den Eigenschaften zuweisen.

    }

    // weitere Methoden, zb. Update, delete etc...

    --------------

    Und der Klassenindexer soll nun ermöglichen, dass ich ebend auf die einzelnen Daten zugreife.

    Also dbKlasse ^dat = gcnew dbKlasse();

    dat[0]->Rang = // Erster Eintrag der Liste Rang
    dat[1]->Bundesland = // Zweiter Eintrag der Liste Bundesland

    usw...

    Ich hoffe, ich konnte es etwas klarer machen... ist wie gesagt eine Idee in meinem Kopf und dachte der Klassenindexer könnte das einfacher machen...

    Gruß Solick



  • Codetags

    public class dbKlasse {
    
    List<int> Rang;
    List<String ^> ^Bundesland;
    List<float> Einwohner;
    
    dbKlasse(Rang, Bundesland, Einwohner)
    {
    // hier dann die Werte aus der Datenbank auslesen und nacheinander den Eigenschaften zuweisen.
    
    }
    
    // weitere Methoden, zb. Update, delete etc...
    
    --------------
    
    Und der Klassenindexer soll nun ermöglichen, dass ich ebend auf die einzelnen Daten zugreife.
    
    Also dbKlasse ^dat = gcnew dbKlasse();
    
    dat[0]->Rang = // Erster Eintrag der Liste Rang
    dat[1]->Bundesland = // Zweiter Eintrag der Liste Bundesland
    


  • Ja, das ist natürlich machbar, genau mit der default-property die ich bereits erwähnt habe. Allerdings musst du, um dieses Verhalten zu bekommen, eine Art Proxy zurückgeben, in welchem du den Index, sowie einen Verweis auf die Hauptinstanz speicherst. Dem Proxy gibtst du dann weitere Properties, welche über den Index auf einen Eintrag in der Hauptinstanz zugreifen...

    public ref class dbKlasse
    {
        List<...> ^foobar;
        // ...
    public:
        ref class Proxy
        {
            dbKlasse ^haupt; 
            int index;
        public:
            Proxy(dbKlasse ^, int);
            property ... foobar()
            {
                ... get()
                {
                    return haupt->foobar[index];
                }
            }
        };
        property Proxy default[int]
        {
            Proxy ^get(int index)
            {
                return gcnew Proxy(this, index);
            }
        }
    };
    

    Nur schnell zusammengeklimpert, aber im Prinzip funktioniert das so^^

    MfG



  • Ah. Danke.

    Verstehe ich das richtig:

    Innerhalb der dbKlasse wird die Proxy-Klasse definiert.

    Die Eigenschaften werden in der dbKlasse definiert.

    Die Zugriffe erfolgen aber aus der Proxy-Klasse durch die dort erzeugte Instanz der dbKlasse.

    Frage:

    Gibt es dann überhaupt Methoden in der dbKlasse oder wird das alles über die Proxy-Klasse gesteuert?

    der default property wird neben der Proxy-Klasse aber in der dbKlasse definiert?

    Gruß Solick



  • Du implementierst in der dbKlasse die Methoden für alle deine Funktionalität; in der Proxyklasse speicherst du das Indexargument, mit welchem du alle diese Methoden in der Hauptklasse aufrufst. Auf diese Weise sparst du dir die explizite Angabe der Indices - und kannst, da die meisten Methoden dann keine explizite Argumente mehr brauch, die Funktionen durch Properties ersetzen.

    MfG



  • Ich habs mal probiert, komme aber mit den Rückgabetypen der getter nicht weiter, da meckert er immer rum das das nicht dem Typ List<T> entspricht...

    #pragma once
    
    ref class dbwrap
    {
    	System::Collections::Generic::List<System::String ^> ^dbBundesland;
    	System::Collections::Generic::List<int> dbRang, dbAktData;
    	System::Collections::Generic::List<float> dbEinwohner;
    
    public:
    	ref class Proxy
    	{
    		dbwrap ^haupt;
    		int index;
    	public:
    		Proxy(dbwrap ^, int);
    
    		property System::Collections::Generic::List<System::String ^> Bundesland
    		{
    			System::Collections::Generic::List<System::String ^> get()
    			{
    				return(haupt->dbBundesland[index];)
    			}
    			void set(System::Collections::Generic::List<System::String ^> ^s)
    			{
    				haupt->dbBundesland[index] = s;
    			}
    		}
    
    		property System::Collections::Generic::List<int> Rang
    		{
    			System::Collections::Generic::List<int> get()
    			{
    				return(haupt->dbRang[index];)
    			}
    			void set(System::Collections::Generic::List<int> i)
    			{
    				haupt->dbRang[index] = i;
    			}
    		}
    
    		property System::Collections::Generic::List<float> Einwohner
    		{
    			System::Collections::Generic::List<float> get()
    			{
    				return haupt->dbEinwohner[index];
    			}
    			void set(System::Collections::Generic::List<float> f)
    			{
    				haupt->dbEinwohner[index] = f;
    			}
    		}
    
    		property System::Collections::Generic::List<int> CountDB
    		{
    			System::Collections::Generic::List<int> get()
    			{
    				return haupt->dbAktData[index];
    			}
    			void set(System::Collections::Generic::List<int> i)
    			{
    				haupt->dbAktData[index] = i;
    			}
    		}
    
    		property Proxy default[int]
    		{
    			dbwrap::Proxy^ get(int index)
    			{
    				return gcnew Proxy(this,index);
    			}
    		}
    
    	};
    }
    


  • Richtig. Du gibst ja im Proxy auch nicht mehr Listen, sondern deren Elemente zurück. Das Proxy benutzt den Index, welchen du in der default-property angegeben hast, um die Listen in der Hauptklasse zu indizieren. Du bist schon nahe dran gewesen...:

    ref class Proxy
        {
            dbwrap ^haupt;
            int index;
        public:
            Proxy(dbwrap ^, int);
    
            property System::String ^Bundesland
            {
                System::String ^get()
                {
                    return(haupt->dbBundesland[index];)
                }
                void set(System::String ^s)
                {
                    haupt->dbBundesland[index] = s;
                }
            }
            // etc...
    


  • So, ich nähere mich langsam...

    #pragma once
    
    ref class dbwrap
    {
    	System::Collections::Generic::List<System::String ^> ^dbBundesland;
    	System::Collections::Generic::List<int> dbRang, dbAktData;
    	System::Collections::Generic::List<float> dbEinwohner;
    
    public:
    	ref class Proxy
    	{
    		dbwrap ^haupt;
    		int index;
    
    	public:
    		Proxy(dbwrap ^, int);
    
    		property System::String ^Bundesland
    		{
    			System::String ^ get()
    			{
    				return haupt->dbBundesland[index];
    			}
    			void set(System::String ^s)
    			{
    				haupt->dbBundesland[index] = s;
    			}
    		}
    
    		property int Rang
    		{
    			int get()
    			{
    				return haupt->dbRang[index];
    			}
    			void set(int i)
    			{
    				haupt->dbRang[index] = i;
    			}
    		}
    
    		property float Einwohner
    		{
    			float get()
    			{
    				return haupt->dbEinwohner[index];
    			}
    			void set(float f)
    			{
    				haupt->dbEinwohner[index] = f;
    			}
    		}
    
    		property int CountDB
    		{
    			int get()
    			{
    				return haupt->dbAktData[index];
    			}
    			void set(int i)
    			{
    				haupt->dbAktData[index] = i;
    			}
    		}
    
    		property Proxy default[int]
    		{
    			Proxy ^get(int index)
    			{
    				return gcnew Proxy(this,index);
    			}
    
    		void set(dbwrap ^h,int index)
    			{
    
    			}
    		}
    
    	};
    }
    

    Was noch nicht geht ist der default property für Proxy, da meckert er, dass get und set nicht definiert sei und dass Proxy ^ nicht dem Rückgabewert dbwrap::Proxy entspricht...


Anmelden zum Antworten