Falschzählung trotz "base" in Visual C#

Hellmut1956

Cadet 4th Year
Registriert
Juni 2011
Beiträge
65
Hallo Freunde

Ich versuche gerade mich in Visual C# 2010 unter Visual Studio 2010 Ultimate einzuarbeiten und folge dabei dem sonstt sehr wertvollen Lehrbuch!

Ich versuche gerade korrekt die Verwendung von "base" zur expliziten Referenzierung einer Methode in der Basis-Klasse anzuwenden, halte mich dabei an die Programmierbeispiele des Lehrbuches. Der Einfachheit des Lernens zuliebe ist das Programm noch eine Konsolen-Anwendung, um so den Fokus auf das Lernen von Visual C# zu legen!

Das Programm besteht zu diesem Zeitpunkt aus 3 Dateien, "Program.cs", "Circle.cs" und "GraphicCircle.cs", wobei die beiden graphischen Dateien mit "Circle" die Basisklasse und GraphicCircle" die davon abgeleitete Klasse darstellt.

Mein Problem, eine Zählgröße "CountCircles" zeigt, siehe Testkode in "Program.cs", trotz expliziter Referenzierung auf die Methode in der Basisklasse Circle, als Ergebnis eine "2" und nicht eine "1"! Vielleicht kann mir jemand sagen, was ich falsch mache, bzw. offensichtlich noch nicht richtig verstanden habe! es folgt der Code aus den 3 Dateien:

Program.cs

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeometricObjects
{
    class Program 
    {  
        static void Main(string[] args) 
        {    
                    GraphicCircle gc = new GraphicCircle();
                    Console.WriteLine("Anzahl der Kreise = {0}", 
                        GraphicCircle.CountCircles); 
            Console.ReadLine();  }}

}

Basisklasse Circle:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeometricObjects
{
    // ------------------------------------------------------------------
    // Beispiel: ...\Kapitel 3\GeometricObjectsSolution
    // ------------------------------------------------------------------
    public class Circle 
    {  
        // ---------- Felder -------------  
        protected double _Radius;  
        public int XCoordinate 
        { 
            get; 
            set; 
        }  
        public int YCoordinate 
        { 
            get; 
            set; 
        }  
        // --------- Statisches Feld -----------  
        protected static int _CountCircles;  
        // --------- Konstruktoren --------------- 
        public Circle() 
        {    
            _CountCircles++;  
        }  
        public Circle(double radius) : this() 
        {    
            Radius = radius;  
        }  
        public Circle(double radius, int xPos, int yPos) :this(radius) 
        {    
            XCoordinate = xPos;    
            YCoordinate = yPos;  
        }  
        // -------- Eigenschaftsmethoden ---------- 
        public double Radius 
        {    
            get 
            { 
                return _Radius; 
            }    
            set 
            {      
                if (value >= 0)        
                    _Radius = value;      
                else        
                    Console.WriteLine("Unzulässiger negativer Radius.");    
            }  
        }  
        // ---------- Klasseneigenschaft ----------------- 
        public static int CountCircles 
        {    
            get 
            { 
                return _CountCircles; 
            }  
        }  
        // ---------- Instanzmethoden ---------- 
        public double GetArea() 
        {    
            return 3.14 * Math.Pow(Radius, 2);  
        } 
        public double GetCircumference() 
        {    
            return 2 * 3.14 * Radius;  
        } 
        public int Bigger(Circle kreis) 
        {    
            if (Radius < kreis.Radius)      
                return -1;    
            else 
                if (Radius == kreis.Radius)      
                    return 0;    
                else      
                    return 1;  
        } public void MoveXY(int dx, int dy)
        {    
            XCoordinate += dx;    
            YCoordinate += dy;  
        } 
        public void MoveXY(int dx, int dy, int dRadius) 
        {    
            XCoordinate += dx;    
            YCoordinate += dy;    
            Radius += dRadius;  
        }  
        // -------- Klassenmethoden ------------ 
        public static double GetArea(int radius) 
        {    
            return 3.14 * Math.Pow(radius, 2);  
        }    
        public  static double GetCircumference(double radius) 
        {    
            return 2 * 3.14 * radius;  
        } 
        public static int Bigger(Circle kreis1, Circle kreis2) 
        {    
            if (kreis1.Radius < kreis2.Radius)      
                return -1;    
            else 
                if (kreis1.Radius == kreis2.Radius)      
                    return 0;    
                else      
                    return 1;  
        }
    }

}

Und abgeleitete Klasse GraphicCircle:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeometricObjects
{
    public class GraphicCircle : Circle
    { 
        public GraphicCircle() 
        { 
            _CountCircles++; 
        } 
        public GraphicCircle(double radius) : base(radius) 
        { 
            Radius = radius; 
        } 
        public GraphicCircle(double radius, int xPos, int yPos) : base(radius) 
        { 
            XCoordinate = xPos; 
            YCoordinate = yPos; 
        } 
    }
}
 
Zuletzt bearbeitet:
Du hast falsch kopiert. Füge mal bitte noch die beiden Klassen ein.
 
Weil die Zeile
Code:
GraphicCircle gc = new GraphicCircle();
den parameterlosen Konstruktor der Klasse "GraphicCircle" und dieser implizit auch den der Klasse "Circle" aufruft. Jeder der beiden Konstruktoren erhöht die Anzahl um 1 weshalb 2 als Ausgabe richtig ist.

Korrekt müsste der parameterlose Konstruktor in GraphicCircle auch den Konstruktor der Basisklasse aufrufen:
Code:
public class GraphicCircle : Circle
{ 
    public GraphicCircle() : base()
    { 
    } 

    public GraphicCircle(double radius) : base(radius) 
    { 
        Radius = radius; 
    } 

    public GraphicCircle(double radius, int xPos, int yPos) : base(radius) 
    { 
        XCoordinate = xPos; 
        YCoordinate = yPos; 
    } 
}
 
doctor84 schrieb:
Wobei man sich das base() sparen kann.
Stimmt, ich schreibe es aber zumindest bei mehreren Überladungen auch hin. So ist immer auf den ersten Blick klar welcher Konstruktor benutzt wird.
Ist eine reine Geschmacksfrage, aber gerade hier (zu Lernzwecken) finde ich verdeutlicht es nochmal den Sachverhalt.
 
In den Konstruktoren von GraphicCircle müsste doch bei base(...) nichts mehr stehen oder?
 
doctor84 schrieb:
In den Konstruktoren von GraphicCircle müsste doch bei base(...) nichts mehr stehen oder?
Nein, wieso?
Wenn du Konstruktoren mit "base" (oder auch "this") verkettest wird zunächst der Basiskonstruktor aufgerufen. Nachdem dieser fertig ist, wird der Code im eigentlichen Konstruktor ausgeführt.
 
@TheCadillacMan: Ich sehe, bis auf das beim Konstruktor:

public GraphicCircle()
{
_CountCircles++;
}

die Zeile Kode drin steht bei mir keinen Unterschied zu deinem Kode! Ich hatte ja, wie man im Code sieht das "base" ja schon drin und trotzdem zeigt der Zähler "2" an!

Ich steh wohl irgendwo auf der Leitung!
 
public GraphicCircle() ruft automatisch sein base(), also public Circle().
So wird zweimal _CountCircles++ ausgeführt.

Da du in
public Circle(double radius) : this()

mit this() wiederum GraphicCircle() aufrufst, wird auch wieder implizit Circle() aufgerufen (Stichwort parameterloser Konstruktor).

Mach statt
public GraphicCircle()
{
_CountCircles++;
}

einfach
public GraphicCircle()
{
}

Mit oder ohne base() - der Aufruf wird stattfinden.

Gruß
 
@Hellmut1956:
Beim Erzeugen einer Instanz einer abgeleiteten Klasse werden immer auch die Konstruktoren der Basisklassen aufgerufen. In deinem Fall wird erst der Konstruktor der Basisklasse "Circle" aufgerufen, welcher "_CountCircles++" ausführt. Dann wird der Konstruktor der Klasse GraphicCircle aufgerufen, der wiederum "_CountCircles++". "_CountCircles" wird also zwei mal um 1 erhöht.
In den Konstruktor einer abgeleiteten Klasse gehört also nur zusätzliche Logik für diese Ableitung. Die Logik der Basisklasse wird wie gesagt automatisch ausgeführt.

doctor84 schrieb:
Und im Basiskonstruktor steht das gleiche.
Oh, ja stimmt. Das hab ich wohl übersehen. Bei den anderen beiden Konstruktoren ist es im konkreten Fall aber egal, da es ja nur die gleichen Zuweisungen wie im Basiskonstruktor sind. Aus Sicht der Logik und der Wartbarkeit ist es so natürlich Quatsch. So ist es richtig:
Code:
public class GraphicCircle : Circle
{ 
    public GraphicCircle() : base()
    { 
    } 

    public GraphicCircle(double radius) : base(radius) 
    { 
    } 

    // Anmerkung hier würde ich auch die entsprechende Überladung der Basisklasse aufrufen:
    // statt: public GraphicCircle(double radius, int xPos, int yPos) : base(radius)
    // besser:
    public GraphicCircle(double radius, int xPos, int yPos) : base(radius, xPos, yPos)
    { 
    } 
}
 
@TheCadillacman: Danke, deine Antwort war für mich sehr hilfreich. Sicher ist das Beispielprogramm in Lehrbuch sehr darauf konzentriert die Anschaulichkeit zu pflegen und daher nur auf dem bereits vorgestelltem aufzubauen! Ich hatte die "Mechanik" der Aufrufe verstanden und daher hier ein Verständnisproblem, warum, obwohl das Lehrbuch es nach meiner Lesart anders sagt, trotzdem der Zähler mit 2 erschien. Dank deiner Auskunft habe ich verstanden, das ich als "gute Praxis" darauf achten sollte Methode in abgeleiteten Klassen nur bei Erweiterungen schreiben sollte, richtig?
 
Zurück
Oben