Autor Beitrag
Testobjekt
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 15:59 
Hallo!
Ich bekomme eine stack overflow exception, beim Programmstart. Und zwar dann, wenn ich in meiner Klasse meine Hauptform instanziere.
( frm_spiel schiffsvarianten = new frm_spiel(); ) <- Hier
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
    class cls_schiffsarten
    {
        frm_spiel schiffsvarianten = new frm_spiel();
        //cls_schiffsteil[] m_teile;
        //int m_schiffsindex;
        int x = 0;
        int y = 0;
        public cls_schiffsarten()
        {
            Point mousePoint = schiffsvarianten.m_spielfeldhome[x, y].PointToClient(Control.MousePosition);
            x = mousePoint.X / 50;
            y = mousePoint.Y / 50;            
        }

Ich weiß nicht wo sonst ich dies instanzieren könnte da ich in der Klasse mehrere Methoden habe wo ich aus meiner Hauptform diese aufrufen muss. Hier ein Beispiel aus dem Hauptform:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
    public void home_Click(object sender, EventArgs e)
        {
            if (rdb_uboot.Checked == true)
                schiffe.Uboot();
            else if(rdb_Zerstoerer.Checked == true)
                schiffe.Zerstoerer();


In meiner Klasse rufe ich so 4 Methoden auf.
ausblenden C#-Quelltext
1:
2:
3:
4:
        public void Uboot()
        {
            schiffsvarianten.m_spielfeldhome[x, y].Image = System.Drawing.Image.FromFile(@"Versenkt.jpg");
        }


Weiß jemand wie ich diesen Fehler beheben kann?
Ich dachte mir ich lagere diese ganzen Methoden in einer eigenen Klasse aus um es übersichtlicher zu gestalten. Seitdem auslagern kommt diese Fehlermeldung.
Für rasche antworten bedanke ich mich bereits im Voraus. :)

Moderiert von user profile iconTh69: C#-Tags hinzugefügt
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 11.01.15 16:28 
Aus dem gezeigten kann man nicht viel ableiten. Zeig uns zumindest noch den Konstruktor von frm_spiel udn wie die schiffe Variable ihre Referenz (auf cls_schiffsarten?) bekommt.

Von dem wenigen was ich da sehe bekomme ich den Eindruck das sich 2 Instanzen der Klassen cls_schiffsarten und frm_spiel gegenseitig kennen müssen. Wenn dem so ist dann würde ich dir aus Erfahrung sagen das ein solches Muster die Wurzel vieler Probleme ist, auch wenn ich hier im konkreten Fall da nicht eindeutig mit dem Finger darauf zeigen und behaupten kann das das die Ursache des Stack Overflows ist. Du solltest aber definitiv dahin arbeiten ausschließlich gerichtete Beziehung zu haben aber keine wechselseitigen.
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 16:46 
Im frm_spiel Konstruktor habe ich nichts deklariert. Durch deine Antwort bin ich aber auf die Idee gekommen, dass ich meine Klasse nur in meinem Home_Click Event brauche, vorhin war sie direkt in frm_spiel instanziert. Und siehe da, dass Programm lässt sich öffnen.
Nun wenn ich im Programm auf meine Picturebox klicke (ein Schiff erstellen will) kommt eine NullReferenceException..
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
       public cls_schiffsarten()
        {
            //string schiff = "Versenkt.jpg";

            //m_schiffsindex = schiffsindex;

            Point mousePoint = schiffsvarianten.m_spielfeldhome[x, y].PointToClient(Control.MousePosition);
            x = mousePoint.X / 50;
            y = mousePoint.Y / 50;
        }


Ich versteh nicht warum meine X und Y Koordinate 0 anzeigen. Sie sollten durch meine MousePosition ja einen Wert bekommen haben. Ich wüsste nicht wo ich sonst das mit dem mousePoint machen könnte als im Konstruktor?

Wie im vorigen Beispiel gezeigt habe ich in der Klasse x und y mit 0 deklariert. Kann das vom Konstruktor nicht überschrieben werden?
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 11.01.15 16:50 
Zitat:
Wie im vorigen Beispiel gezeigt habe ich in der Klasse x und y mit 0 deklariert. Kann das vom Konstruktor nicht überschrieben werden?


Natürlich geht das. Ich vermute mal die Lösung zum Problem liegt in der Frage die du nicht beantwortet hast ;)

Zitat:
.... und wie die schiffe Variable ihre Referenz (auf cls_schiffsarten?) bekommt.
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 16:57 
Habe sie nur im Event instanziert.
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
public void home_Click(object sender, EventArgs e)
        {
            cls_schiffsarten schiffe = new cls_schiffsarten();

            if (rdb_uboot.Checked == true)
                schiffe.Uboot();
            else if (rdb_Zerstoerer.Checked == true)
                schiffe.Zerstoerer();
            else if (rdb_Kreuzer.Checked == true)
                schiffe.Kreuzer();
            else if (rdb_schlachtschiff.Checked == true)
                schiffe.Schlachtschiff();
        }
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 11.01.15 17:16 
Du hast offensichtlich ein Problem bei der Unterscheidung Klasse und Instanz einer Klasse.

In home_Click erzeugst du eine neue Instanz der Klasse cls_schiffsarten. Diese erzeugt sich eine neue Instanz der Klasse frm_spiel (das ist nicht die gerade cls_schiffsarten erzeugt hat sondern du erzeugst ja eine neue in der Klasse). Dieses frm_spiel dieser cls_schiffsarten Instanz wird dann im Konstruktor benutzt und hat ja nichts mit der zu tun die gerade angezeigt wird. Was du da dann von dieser Klasse ausliest ist Konstant du rufst ja irgendwas von m_spielfeldhome ab von einer frm_spiel Instanz die nie angezeigt wurde und deren m_spielfeldhome nur das enthält was du im Designer festgelegt hast.

Wenn sich 2 konkrete Instanzen von cls_schiffsarten und frm_spiel gegenseitig kennen mußt du beim erzeugen oder nach dem erzeugen einer der beiden einer Referenz auf die schon vorhandene Klasse dem anderen mitgeben aber nicht dort einfach einen neue Klasse erzeugen. So baust du nur eine endlose Kette (daher der Stack Overflow) von Klasseninstanzen cls_schiffsarten -> frm_spiel -> cls_schiffsarten -> frm_spiel -> cls_schiffsarten -> frm_spiel -> cls_schiffsarten ... ad infinitum auf.
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 17:24 
Okay ich denke ich habs verstanden.
Ich erkenne nun zwar den Fehler, doch wie kann ich das Problem beheben?
Wie übergebe ich meiner Klasse diese Referenz damit die sich identifizeren können? :eyecrazy:
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 11.01.15 17:45 
Wie ist deine Anwendung aufgebaut bzw. was gibt es zuerst? frm_spiel oder cls_schiffsarten?
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 17:49 
Ich beginne mit der Hauptform Anwendung frm_spiel.

Erst wenn ich im frm_spiel mit einer picturebox (m_spielfeldhome[x,y]) interagiere wird cls_schiffsarten genutzt.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: So 11.01.15 17:53 
Es ist nicht ganz klar, warum du das Formular (frm_spiel) bei den Varianten (in cls_schiffsarten) benötigst. Ich würde einfach die x- und y-Werte übergeben, wenn die in der Methode gebrauchst werden.

Wenn das Formular für cls_schiffsarten in jedem Fall benötigt wird, mache einen Konstruktorparameter wo man das übergibt. Erstellen geht dann so:
ausblenden C#-Quelltext
1:
cls_schiffsarten schiffe = new cls_schiffsarten(this);					

Und speichere im Konstruktor den Parameter in einem privaten Feld ab.

Übrigens:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
public void home_Click(object sender, EventArgs e)
{
  cls_schiffsarten schiffe = new cls_schiffsarten();

  //...
}

in den EventArgs sollten schon x und y-Werte drin stehen :wink:


Zuletzt bearbeitet von jfheins am Mo 12.01.15 01:36, insgesamt 1-mal bearbeitet
Testobjekt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 22



BeitragVerfasst: So 11.01.15 18:05 
@jfheins

Bei den versch. Varianten (in cls_schiffsarten) werden meiner Picturebox Bilder übergeben. Ich brauche die PictureBox & die X & Y Koordinaten.

Wenn ich in meinem Formular Konstruktorparameter festlege werden die bei mir nicht übergeben. Ich komm damit nicht ganz klar..
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: So 11.01.15 18:22 
Zitat:
Ich beginne mit der Hauptform Anwendung frm_spiel.


Dann erzeugt also frm_spiel alles was im folgenden gebraucht wird unter anderem dann auch cls_schiffsarten und übergibt sich dann selbst an cls_schiffsarten damit diese mit dieser Instanz arbeiten kann.
Zum Beispiel über den Konstruktor von cls_schiffsarten.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
private frm_spiel _owningForm; // Klassenvariable

public cls_schiffsarten(frm_spiel owningForm)
{
    _owningForm = owningForm;

    Point mousePoint = _owningForm.m_spielfeldhome[x, y].PointToClient(Control.MousePosition);
    x = mousePoint.X / 50;
    y = mousePoint.Y / 50;            
}


Aber jfheins hat höchstwahrscheinlich recht in der Annahme das du in cls_schiffsarten Dinge tust die da einfach nicht hingehören und in frm_spiel bleiben sollen. Zum Beispiel die Koordinaten im Konstruktor beim erzeugen festzulegen ist irgendwie sinnfrei. Die werden sich ja danach nie wieder ändern. Die Koordinaten sollen doch garantiert die sein wohin geklickt wurde. Sollten also in home_Click ermittelt werden und nicht irgendwann beim initialisieren der Anwendung bzw. dieser Klasse. Und dann sollten die Koordinaten auch relativ zu dem Control ermittelt werden auf das geklickt wurde jetzt ermittelst du die scheinbar relativ zu den Koordinaten wo zuvor drauf geklickt wurde. Das sieht auch irgendwie eher sinnfrei aus ;)

Edit:
Zitat:
in den EventArgs sollten schon x und y-Werte drin stehen :wink:

@jfheins: Bei welchem Control liefert Click denn Koordinaten mit? Bei den meisten Controls ist es ziemlich egal wo auf das Control geklickt wurde, zumindest mir ist egal wo genau ein User auf z.B. einen Button geklickt hat. Hauptsache er hat den Button getroffen ;)
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 12.01.15 01:38 
Zitat:
Wenn ich in meinem Formular Konstruktorparameter festlege werden die bei mir nicht übergeben. Ich komm damit nicht ganz klar..

Du sollst auch nicht dem Konstruktor des Formulars Parameter hinzufügen, sondern den anderen Konstruktor (dem von cls_schiffsarten) siehe auch meine Korrektur oben:
ausblenden C#-Quelltext
1:
cls_schiffsarten schiffe = new cls_schiffsarten(this);					

Wenn da noch kein Konstruktor ist, dann eben einen erstellen ;-)

@Ralf: Viele Controls müssten ein MouseClick Event haben. Dort bekommt man dann MouseEventArgs wo wieder X und Y drin sind.