Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 6 101 -Klassen
  gp 6.1 Eine einfache Klasse
  gp 6.2 Mitgliedsfunktionen
  gp 6.3 Ref- und Out-Parameter
  gp 6.4 Überladung

Kapitel 6 101 -Klassen

In einer objektorientierten Sprache sind Klassen das Herz jeder Anwendung. Dieses Kapitel ist in verschiedene Abschnitte gegliedert. Der erste Abschnitt beschreibt die Elemente von C#, die häufig eingesetzt werden, die folgenden Abschnitte behandeln seltener verwendete Bestandteile, je nach dem, welche Art von Code geschrieben wird.


Galileo Computing

6.1 Eine einfache Klasse  downtop

Eine C#-Klasse kann sehr einfach sein:

class VerySimple
{
    int    simpleValue = 0;
}
class Test
{
    public static void Main()
    {
        VerySimple vs = new VerySimple();
    }
}

Diese Klasse stellt einen Container für einen einzigen integer-Wert dar. Da der integer-Wert deklariert wird, ohne Informationen für den Zugriff bereitzustellen, handelt es sich um einen privaten Wert der VerySimple-Klasse, der außerhalb der Klasse nicht referenziert werden kann. Mit dem private-Modifikator könnte dies explizit ausgedrückt werden.

Der integer-Wert simpleValue ist ein Mitglied der Klasse; es können verschiedene Mitgliedstypen vorliegen.

In der Main()-Funktion erstellt das System die Instanz im Heapspeicher, ersetzt alle Datenmitglieder der Klasse durch Nullen und gibt einen Verweis auf die Instanz zurück. Ein Verweis ist eine einfache Methode, sich auf eine Instanz zu beziehen.

Es muss nicht angegeben werden, dass eine Instanz nicht länger benötigt wird. Im vorangegangenen Beispiel ist der Verweis auf die Instanz nach Beenden der Funktion Main() nicht mehr vorhanden. Wenn der Verweis nicht an anderer Stelle gespeichert wurde, kann die Instanz von der Speicherbereinigung zurückgefordert werden. Durch die Speicherbereinigung wird der zugewiesene Speicher ggf. ebenso zurückgefordert.

Dies alles ist sehr schön, aber die Klasse vollbringt nichts Nützliches, da der integer-Wert nicht zugänglich ist. Hier ein etwas sinnvolleres Beispiel:

using System;
class Point
{
        // Erstellungsroutine
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

        // Mitgliedsfelder
    public int x;
    public int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        Console.WriteLine("myPoint.x {0}", myPoint.x);
        Console.WriteLine("myPoint.y {0}", myPoint.y);
    }
}

In diesem Beispiel liegt die Klasse Point vor, die zwei integer-Werte in der Klasse x und y aufweist. Diese Mitglieder sind als öffentlich deklariert, d. h., die zugehörigen Werte sind allen Codeabschnitten zugänglich, die die Klasse verwenden. Zusätzlich zu den Datenmitgliedern ist eine Erstellungsroutine für diejenige Klasse vorhanden, bei der es sich um eine besondere Funktion handelt, welche aufgerufen wird, um eine Instanz der Klasse zu erstellen. Die Erstellungsroutine erfordert zwei integer-Parameter.

In dieser Erstellungsroutine wird die besondere Variable this verwendet; diese this-Variable steht allen Mitgliedsfunktionen zur Verfügung und bezieht sich immer auf die aktuelle Instanz. In Mitgliedsfunktionen sucht der Compiler zunächst nach lokalen Variablen und Parametern für einen Namen, bevor nach Instanzmitgliedern gesucht wird. Bei der Referenzierung einer Instanzvariablen, für die ein gleichnamiger Parameter vorliegt, muss die Syntax this.<Name> verwendet werden.

In dieser Erstellungsroutine verweist x selbst auf den Parameter x, und this.x verweist auf das integer-Mitglied namens x.

Neben der Point-Klasse ist eine Test-Klasse vorhanden, die eine Main-Funktion zum Starten des Programms enthält. Die Main-Funktion erstellt eine Instanz der Point-Klasse, wodurch Speicher für das Objekt zugewiesen und die Erstellungsroutine für die Klasse aufgerufen wird. Die Erstellungsroutine setzt die Werte für x und y.

In den verbleibenden Zeilen von Main() werden die Werte für x und y ausgedruckt.


Galileo Computing

6.2 Mitgliedsfunktionen  downtop

Die Erstellungsroutine im vorherigen Codeabschnitt ist ein Beispiel für eine Mitgliedsfunktion, also ein Codeabschnitt, der für eine Instanz des Objekts aufgerufen wird. Erstellungsroutinen können automatisch aufgerufen werden, wenn mit Hilfe von new eine Objektinstanz erstellt wird.

Andere Mitgliedsfunktionen können folgendermaßen deklariert werden:

using System;
class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

        // Zugriffsfunktionen
    public int GetX() {return(x);}
    public int GetY() {return(y);}

        // Variablen jetzt privat
    int x;
    int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        Console.WriteLine("myPoint.X {0}", myPoint.GetX());
        Console.WriteLine("myPoint.Y {0}", myPoint.GetY());
    }
}

In diesem Beispiel wird direkt auf die Datenfelder zugegriffen. Dies ist üblicherweise nicht empfehlenswert, da der Benutzer der Klasse so von den Feldnamen abhängt und dadurch spätere Änderungen erschwert werden.

In C# werden Mitgliedsfunktionen nicht so entworfen, dass sie auf private Werte zugreifen. Stattdessen wird eine Eigenschaft verwendet, die die Vorteile einer Mitgliedsfunktion bietet und gleichzeitig das Benutzermodell für ein Feld wahrt. Weitere Informationen hierzu finden Sie in Kapitel 18, Eigenschaften.


Galileo Computing

6.3 Ref- und Out-Parameter  downtop

Das Aufrufen zweier Mitgliedsfunktionen zum Erhalt der Werte ist nicht immer praktikabel, es wäre eleganter, beide Werte mit nur einem einzigen Funktionsaufruf abzurufen. Es gibt jedoch nur einen Rückgabewert.

Die Lösung besteht darin, Verweisparameter zu verwenden (ref-Parameter), damit die an die Mitgliedsfunktion übergebenen Parameterwerte geändert werden können:

// Fehler
using System;
class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
        // Beide Werte mit einem Funktionsaufruf abrufen
    public void GetPoint(ref int x, ref int y)
    {
        x = this.x;
        y = this.y;
    }

    int x;
    int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        int    x;
        int     y;

            // unzulässig
        myPoint.GetPoint(ref x, ref y);
        Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

In diesem Codeabschnitt wurden die Parameter ebenso wie der Funktionsaufruf mit Hilfe des ref-Schlüsselwortes deklariert.

Dieser Code sollte funktionieren, bei der Kompilierung wird jedoch eine Fehlermeldung erzeugt, nach der für die ref-Parameter x und y nicht initialisierte Werte verwendet wurden. Dies bedeutet, dass Variablen an die Funktion übergeben wurden, bevor deren Werte gesetzt wurden. Der Compiler lässt das Offenlegen von Werten nicht initialisierter Variablen jedoch nicht zu.

Es gibt zwei Möglichkeiten, diesen Fehler zu umgehen. Die erste Möglichkeit besteht darin, die Variablen nach deren Deklaration zu initialisieren.

using System;
class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public void GetPoint(ref int x, ref int y)
    {
        x = this.x;
        y = this.y;
    }

    int x;
    int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        int    x = 0;
        int     y = 0;

        myPoint.GetPoint(ref x, ref y);
        Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

Der Code kann nun kompiliert werden, die Variablen wurden jedoch lediglich mit dem Wert 0 initialisiert, um beim Aufruf von GetPoint() überschrieben zu werden. In C# gibt es eine weitere Option: Sie können die Definition der Funktion GetPoint() so abändern, dass statt eines ref-Parameters der Parameter out verwendet wird.

using System;
class Point
{
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public void GetPoint(out int x, out int y)
    {
        x = this.x;
        y = this.y;
    }

    int x;
    int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        int    x;
        int     y;

        myPoint.GetPoint(out x, out y);
        Console.WriteLine("myPoint({0}, {1})", x, y);
    }
}

Out-Parameter verhalten sich genau wie ref-Parameter, nur dass diesen nicht initialisierte Variablen übergeben werden können und der Aufruf nicht mit ref, sondern mit out erfolgt.


Galileo Computing

6.4 Überladung  toptop

Gelegentlich kann es nützlich sein, über zwei Funktionen zu verfügen, die zwar dem gleichen Zweck dienen, jedoch unterschiedliche Parameter verwenden. Dies trifft besonders auf Erstellungsroutinen zu, denn eine neue Instanz kann häufig auf verschiedene Weise erstellt werden.

class Point
{
        // Neuen Punkt aus den x- und y-Werten erstellen
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
        // Punkt aus einem vorhandenen Punkt erstellen
    public Point(Point p)
    {
        this.x = p.x;
        this.y = p.y;
    }

    int x;
    int y;
}

class Test
{
    public static void Main()
    {
        Point myPoint = new Point(10, 15);
        Point mySecondPoint = new Point(myPoint);
    }
}

Die Klasse verfügt über zwei Erstellungsroutinen. Die eine Routine kann mit x- und y-Werten aufgerufen werden, die andere mit einem weiteren Punkt. Die Main()-Funktion verwendet beide Erstellungsroutinen; eine zum Erstellen einer Instanz aus einem x- und einem y-Wert, die andere zum Erstellen einer Instanz aus einer bereits vorhandenen Instanz.

Wird eine überladene Funktion aufgerufen, wählt der Compiler die geeignete Funktion durch einen Abgleich der Parameter im Aufruf mit den für die Funktion deklarierten Parametern.






1    Für diejenigen unter Ihnen, die üblicherweise Zeiger verwenden: Ein Verweis ist ein Zeiger, der nur zugewiesen und wieder aufgehoben werden kann.

2    Die in der .NET-Laufzeitumgebung verwendete Speicherbereinigung wird in Kapitel 31, C# im Detail, behandelt.

3    Wenn Sie tatsächlich eine eigene point-Klasse implementieren möchten, würden Sie wahrscheinlich eher einen Datentyp (struct) als einen Verweistyp (class) verwenden.

4    Aus der Sicht anderer .NET-Sprachen besteht kein Unterschied zwischen ref- und out-Parametern. Ein C#-Programm, das diese Funktion aufruft, betrachtet die Parameter als out-Parameter, in anderen Sprachen werden die Parameter als ref-Parameter betrachtet.

   

Select * from SQL Server 2000




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de