ein Projekt von goloroden.de
Skip Navigation Linksguide to C# > Guide > Klassen und Strukturen

Klassen und Strukturen

Was sind Klassen?

Da C# eine objektorientierte Sprache ist, wird am häufigsten das Konzept der Klasse zur Erstellung eigener Typen verwendet. Klassen sind, wie bereits erwähnt, Baupläne für Objekte, mit denen Daten modelliert werden können.

Eine Klasse wird in C# mit dem Schlüsselwort class erzeugt, dem der Name der Klasse folgt. Klassen besitzen ebenso wie Namensräume einen durch geschweifte Klammern eingeschlossenen Rumpf, weshalb ihre Definition nicht durch ein Semikolon abgeschlossen wird.

Wie bei Namensräumen, so gibt es auch bei Klassen Richtlinien, wie deren Namen gebildet werden. Ein Klassenname besteht aus einem oder mehreren Substantiven, die den Zweck der Klasse beschreiben, wobei in der Regel der Singular verwendet wird. Für die Schreibweise gilt Pascal Case.
C#
1
2
3
4
5
6
7
8
using System;

namespace GoloRoden.GuideToCSharp
{
    class ComplexNumber
    {
    }
}
Dieser Code erzeugt eine Klasse zur Darstellung komplexer Zahlen. Komplexe Zahlen zeichnen sich in der Mathematik dadurch aus, dass mit ihnen die Wurzel von -1 berechnet werden kann, was unter Verwendung lediglich reeller Zahlen nicht möglich ist. Die Wurzel aus -1 wird dabei mit der imaginären Einheit i bezeichnet, wobei
i² = -1
gilt. Komplexe Zahlen werden in der Regel in der Form
a + b × i
dargestellt, wobei a als der Real- und b als der Imaginärteil bezeichnet werden. Bevor die Klasse ausgebaut wird, um komplexe Zahlen darstellen und verarbeiten zu können, sollte sie zunächst kommentiert werden.

Prinzipiell gibt es in C# drei Arten von Kommentaren. Die einfachste Variante stellen einzeilige Kommentare dar, die durch einen doppelten Schrägstrich eingeleitet werden, und an einer beliebigen Stelle einer Zeile beginnen können, wobei für einen Kommentar in der Regel eine neue Zeile verwendet wird, um die Übersichtlichkeit zu bewahren.

Einzeilige Kommentare werden im wesentlichen für interne Kommentare des Entwicklers verwendet und kennzeichnen häufig Zeilen im Code, an denen die Arbeit noch nicht abgeschlossen ist.

Außerdem werden einzeilige Kommentare oft verwendet, um die Arbeitsweise von Code zu erläutern, so dass dies auch nach Wochen oder Monaten noch nachvollzogen werden kann, ohne dass eine mühsame Analyse und Einarbeitung erforderlich wäre.

Generell ist es beim Einfügen von Kommentaren ratsam, diese mit einem Datum zu versehen. Vor allem in Teams wird dies zudem häufig durch ein Namenskürzel ergänzt, was Nachfragen erleichtert. Daher wird es im allgemeinen als guter Stil angesehen, wenn Code derart kommentiert wird.

In welcher Sprache kommentiert wird, ist prinzipiell beliebig, allerdings wird oft auf Englisch zurückgegriffen, unter anderem, um in mehrsprachigen Teams über eine einheitliche Kommunikationssprache zu verfügen.
C#
1
2
3
4
5
6
7
8
9
10
using System;

namespace GoloRoden.GuideToCSharp
{
    class ComplexNumber
    {
        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Bei umfangreicheren Kommentaren kann es lästig sein, jede Zeile einzeln durch einen doppelten Schrägstrich einleiten zu müssen. Daher gibt es die zweite Variante von Kommentaren, sogenannte Blockkommentare, die durch einen Schrägstrich gefolgt von einem Stern eingeleitet werden und erst dann enden, wenn sie durch einen Stern gefolgt von einem Schrägstrich wieder geschlossen werden.

Wie viele Zeilen sich innerhalb eines solchen Blockkommentars befinden, ist dabei beliebig. Das Anwendungsgebiet von Blockkommentaren ist dabei aber das gleiche wie das von einzeiligen Kommentaren. Generell gilt für Kommentare, dass sie keine Anweisungen darstellen und daher nicht mit einem Semikolon abgeschlossen werden müssen.
C#
1
2
3
4
5
6
7
8
9
10
using System;

namespace GoloRoden.GuideToCSharp
{
    class ComplexNumber
    {
        /* TODO gr: Add code here.
                    2007-04-08 */
    }
}
Beiden Typen von Kommentaren ist gemein, dass sie nur für den internen Gebrauch gedacht sind. Gerade bei Komponenten, die auch von anderen Entwicklern genutzt werden, ist jedoch eine Trennung in private und öffentliche Kommentare sinnvoll. Die privaten Kommentare werden dabei weiterhin dafür genutzt, den Code mit internen Anmerkungen zu versehen, die öffentlichen Kommentare dienen hingegen als Dokumentation.

Zur Erstellung dieser Dokumentation dient die dritte Variante, die durch drei Schrägstriche eingeleitet wird und durch XML formatiert werden kann, weshalb diese Kommentare gelegentlich auch als XML-Kommentare bezeichnet werden. In diesen Kommentaren werden im Gegensatz zu den anderen Typen weder Datum noch Namenskürzel angegeben.

Außerdem können XML-Kommentare nicht an beliebiger Stelle im Code auftreten, sondern müssen direkt vor dem zu kommentierenden Element stehen. Die Beschreibung einer Klasse wird durch die XML-Elemente <summary> und </summary> eingeschlossen.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    class ComplexNumber
    {
        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Für jede Klasse muss entschieden werden, ob sie nur innerhalb der Assembly genutzt werden können soll, welche die Klasse enthält, oder ob jede Komponente der Anwendung Zugriff erhalten soll. Die Antwort auf die Frage, ob es sinnvoll ist, die Sichtbarkeit einzuschränken, hängt vom Zweck der Klasse ab.

Handelt es sich um eine unterstützende Klasse, die nur innerhalb der Assembly benötigt wird, empfiehlt es sich, die Sichtbarkeit einzuschränken. Ist die Klasse jedoch eine tragende Datenstruktur, die der gesamten Anwendung zur Verfügung stehen soll, wird sie uneingeschränkt zur Verfügung gestellt.

Um die Sichtbarkeit einer Klasse auf die sie enthaltende Assembly zu beschränken, wird ihre Definition mit dem Zugriffsmodifizierer internal versehen. Der öffentliche Zugriff wird erreicht, indem statt dessen der Zugriffsmodifizierer public angegeben wird. Wird auf die Angabe eines solchen Zugriffsmodifizierers verzichtet, ist eine Klasse implizit internal.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Aus Gründen der Übersichtlichkeit wird generell für jede Klasse eine eigene Datei verwendet, deren Name dem der enthaltenen Klasse entspricht. Prinzipiell ist es zwar möglich, innerhalb einer Datei mehrere Klassen zu definieren, dies ist jedoch unüblich und gilt als schlechter Stil.

Zwei Ausnahmen von dieser Regel kommen in C# vor: Partielle Klassen und verschachtelte Klassen. Partielle Klassen, die seit Version 2.0 von C# verfügbar sind, ermöglichen es durch Angabe des zusätzlichen Schlüsselwortes partial bei der Definition der Klasse, eine Klasse auf mehrere Dateien zu verteilen.

Dies wird beispielsweise von Visual Studio genutzt, um vom Benutzer geschriebenen Code und von Visual Studio generierten Code, der sich auf die gleiche Klasse bezieht, voneinander zu trennen, so dass der Benutzer nicht versehentlich generierten Code überschreibt oder verändert, und umgekehrt. Außer in Fällen, in denen generierter und benutzergeschriebener Code gemischt werden, sollte vom Einsatz partieller Klassen abgesehen werden.

Verschachtelte Klassen hingegen ermöglichen, innerhalb einer Klasse eine weitere Klasse zu definieren, genau so, wie auch innerhalb eines Namensraumes ein weiterer Namensraum angelegt werden kann. Im Gegensatz zu Namensräumen ist dies bei Klassen in der Praxis jedoch unüblich, zudem gibt es so gut wie keine Anwendungsfälle, in denen ein solches Verfahren notwendig wäre, weshalb darauf nicht näher eingegangen wird.

Felder

Damit ein Objekt Daten speichern kann, müssen in der zugehörigen Klasse Felder für die einzelnen Daten definiert werden. Felder sollten nur für solche Daten definiert werden, die nicht funktional abhängig von anderen Daten sind - das heißt, lassen sich Daten aus anderen vorhandenen Daten ermitteln, werden sie nicht abgespeichert.

Um eine komplexe Zahl mit der Klasse ComplexNumber abbilden zu können, werden zwei Felder benötigt, nämlich eines für den Real- und eines für den Imaginärteil. Die Frage, von welchem Typ diese Felder sind, ist einfach zu beantworten: Da so wohl Real- wie auch Imaginärteil nach Definition reelle Zahlen sind, werden beide mit Hilfe eines Typs für Dezimalzahlen dargestellt.

Die Bennenung von Feldern erfolgt ähnlich wie die von Klassen, da auch hier der Name aus einem oder mehreren Substantiven gebildet wird und als Gesamtbegriff im Singular steht. Allerdings wird für Felder Camel Case eingesetzt, zudem wird den Namen häufig ein Unterstrich vorangestellt.

Da die Definition eines Feldes in C# eine Anweisung darstellt, wird sie mit einem Semikolon abgeschlossen. Zudem werden auch Felder mit Hilfe von XML-Kommentaren dokumentiert, wobei auch hier wieder das <summary>-Tag zum Einsatz kommt.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        float _realPart;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        float _imaginaryPart;

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Ebenso wie für Klassen, so muss auch für Felder die Sichtbarkeit entschieden werden. Außer internal und public, welche die gleiche Bedeutung wie bei Klassen haben, steht für Felder zusätzlich noch das Schlüsselwort private zur Verfügung. Wird ein Feld als private gekennzeichnet, kann nur aus der Klasse auf das Feld zugegriffen werden, die das Feld enthält.

Im Sinne eines durchgängig objektorientierten Aufbaus einer Anwendung ist es allerdings erforderlich, quasi jedes Feld als private zu markieren, um den direkten Zugriff von außen zu verhindern.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        private float _realPart;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        private float _imaginaryPart;

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Felder können mit Standardwerten versehen werden, indem ihnen bei der Definition der gewünschte Wert zugewiesen wird, wobei dies in der Praxis eher selten angewandt wird, weswegen im folgenden in der Regel darauf verzichtet wird.

Genau genommen wird bei Feldern zwischen Deklaration und Definition unterschieden - während das Feld bei der Deklaration nur der Klasse hinzugefügt wird, wird ihm bei der Definition zusätzlich noch ein Wert zugewiesen. In der Regel wird diese Unterscheidung allerdings nur selten genutzt und generell von Definition gesprochen.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        private float _realPart = 0;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        private float _imaginaryPart = 0;

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Die einzige Ausnahme zu dieser Regel besteht in der Definition eines Feldes mit konstantem Wert, wobei dessen Typ das Schlüsselwort const vorangestellt wird. Der Wert eines solchen konstanten Feldes kann im weiteren Verlauf der Anwendung dann nicht mehr geändert werden. Konstanten werden beispielsweise genutzt, um mathematisch feststehende Werte wie die Zahl Pi oder die Eulersche Zahl zu definieren.
C#
1
private const double _pi = 3.1415926;

Eigenschaften

Da der Zugriff auf Felder, die als private gekennzeichnet wurden, nur noch innerhalb der Klasse möglich ist, stellt sich die Frage, wie Daten eines Objektes überhaupt gelesen oder geschrieben werden können, ohne die Sichtbarkeit des entsprechenden Feldes wieder auf internal oder public ändern zu müssen.

Die Lösung stellen Eigenschaften dar, die zwar nach außen sichtbar sind, aber auf die Felder einer Klasse zugreifen können. Der Unterschied zwischen dem Zugriff auf ein Feld mit Hilfe einer Eigenschaft und dem direkten Zugriff liegt darin, dass die Eigenschaft zusätzliche Prüfungen ausführen kann.

Eine Eigenschaft trägt üblicherweise den gleichen Namen wie das Feld, für das die Eigenschaft zuständig ist. Der einzige Unterschied liegt darin, dass Pascal Case an Stelle von Camel Case verwendet wird und der einleitende Unterstrich entfällt. Zudem ist eine Eigenschaft in der Regel internal oder public, da sie ansonsten von außen nicht sichtbar wäre - dennoch können Eigenschaften theoretisch auch als private gekennzeichnet werden.

Da eine Eigenschaft den Zugriff auf ein Feld gestattet, muss sie über den gleichen Typ verfügen. Der Zugriff an sich erfolgt über zwei Schlüsselwörter, get und set, die für das Auslesen und Schreiben der entsprechenden Daten zuständig sind.

Die einfachste Variante einer Eigenschaft besteht darin, mit get lediglich ein Feld zurückzugeben, ohne weitere Prüfungen auszuführen. Dies geschieht mit Hilfe der Anweisung return, der das zurückzugebende Feld folgt. Ebenso wird mit set nur der zu setzende Wert in das entsprechende Feld geschrieben. Der zu setzende Wert befindet sich dabei in einem Parameter namens value und kann mit Hilfe des Zuweisungsoperators geschrieben werden.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        private float _realPart;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        private float _imaginaryPart;

        public float RealPart
        {
            get
            {
                return _realPart;
            }

            set
            {
                _realPart = value;
            }
        }

        public float ImginaryPart
        {
            get
            {
                return _imaginaryPart;
            }

            set
            {
                _imaginaryPart = value;
            }
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Zudem ist es mit Eigenschaften möglich, ein Feld nur für den Lese- oder nur für den Schreibzugriff freizugeben, indem nur entweder get oder set definiert wird. Außerdem kann seit C# 2.0 entweder get oder set ein stärker einschränkender Zugriffsmodifizierer zugewiesen werden, um beispielsweise den schreibenden Zugriff auf die Klassenebene zu beschränken, den lesenden Zugriff aber auf Anwendungsebene zu gestatten.

Dazu wird entweder get oder set ein entsprechender Zugriffsmodifizierer wie internal oder private vorangestellt. Hierbei muss allerdings beachtet werden, dass dies nur erlaubt ist, wenn eine Eigenschaft so wohl über get wie auch set verfügt, und selbst dann darf ein weiterer Zugriffsmodifizierer nur bei einem der beiden Schlüsselwörter angegeben werden. Das andere behält den Zugriffsmodifizierer, der für die Eigenschaft an sich definiert ist.

Zudem muss der Zugriffsmodifizierer, der get oder set vorangestellt wird, restriktiver sein als der Zugriffsmodifizierer der gesamten Eigenschaft. Wenn also beispielsweise der schreibende Zugriff auf den Realteil einer komplexen Zahl auf die Klasse beschränkt werden soll, muss dem set ein private vorangestellt werden.
C#
1
2
3
4
5
6
7
8
9
10
11
12
public float RealPart
{
    get
    {
        return _realPart;
    }

    private set
    {
        _realPart = value;
    }
}
Auch Eigenschaften werden mit Hilfe von XML-Kommentaren dokumentiert, wobei ein Kommentar mit einem <summary>-Tag zum Einsatz kommt. Zusätzlich enthält der Kommentar für eine Eigenschaft aber noch eine Beschreibung der Daten, die von der Eigenschaft ausgelesen beziehungsweise gesetzt werden. Dies geschieht mit Hilfe des <value>-Tags.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        private float _realPart;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        private float _imaginaryPart;

        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get
            {
                return _realPart;
            }

            set
            {
                _realPart = value;
            }
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get
            {
                return _imaginaryPart;
            }

            set
            {
                _imaginaryPart = value;
            }
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Als funktional abhängige Eigenschaft bietet sich der Betrag einer komplexen Zahl an, der aus dem Real- und dem Imaginärteil ermittelt werden kann, indem beide quadriert und addiert werden und aus dem Ergebnis die Wurzel gezogen wird.
|z| = sqrt(a2 + b2)
Da mathematische Operatoren noch nicht behandelt wurden, wird die entsprechende Eigenschaft an dieser Stelle nur als Platzhalter eingefügt, wobei als Ergebnis vorerst immer 0 zurückgegeben wird. Zusätzlich wird die Eigenschaft mit einem Kommentar versehen, der darauf hinweist, dass die Arbeit an diesem Codeabschnitt noch nicht abgeschlossen ist. Da lediglich das Auslesen des Betrages Sinn ergibt, wird für diese Eigenschaft nur get definiert, so dass ein schreibender Zugriff nicht möglich ist.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Contains the real part.
        /// </summary>
        private float _realPart;

        /// <summary>
        /// Contains the imaginary part.
        /// </summary>
        private float _imaginaryPart;

        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get
            {
                return _realPart;
            }

            set
            {
                _realPart = value;
            }
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get
            {
                return _imaginaryPart;
            }

            set
            {
                _imaginaryPart = value;
            }
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate absolute value.
                //          2007-04-08
                return 0;
            }
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Obwohl es möglich ist, innerhalb von get und set weitere Anweisungen unterzubringen, enthalten die meisten Eigenschaften lediglich die Minimalvariante zum Lesen und Schreiben eines Feldes. Seit der Version 3.0 von C# gibt es für solche Standardeigenschaften eine verkürzte Schreibweise, so dass an Stelle von
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a foo class.
    /// </summary>
    public class Foo
    {
        /// <summary>
        /// Contains a bar value.
        /// </summary>
        private object _bar;

        /// <summary>
        /// Gets or sets the bar value.
        /// </summary>
        /// <value>The bar value.</value>
        public object Bar
        {
            get
            {
                return this._bar;
            }

            set
            {
                this._bar = value;
            }
        }
    }
}
auch die kürzere Variante
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a foo class.
    /// </summary>
    public class Foo
    {
        /// <summary>
        /// Gets or sets the bar value.
        /// </summary>
        /// <value>The bar value.</value>
        public object Bar
        {
            get;
            set;
        }
    }
}
geschrieben werden kann. Semantisch sind beide Varianten identisch, allerdings stellt sich bei der verkürzten Schreibweise die Frage, auf welches Feld mit Hilfe der Eigenschaft zugegriffen wird. Die Antwort auf diese Frage lautet, dass C# intern ein Feld anlegt, dessen Name dem Entwickler nicht bekannt ist, weshalb auf dieses Feld ausschließlich über die Eigenschaft zugegriffen werden kann.

Methoden

Während Eigenschaften zwar geeignet sind, auf Felder lesend und schreibend zuzugreifen, sind ihre Möglichkeiten, andere Aufgaben auszuführen, eher gering. Außerdem beziehen sich Eigenschaften immer nur auf jeweils ein Feld, allerdings kann es vorkommen, dass mehrere Werte verarbeitet werden müssen. Für diese Fälle, die über einen reinen Datenzugriff hinausgehen, gibt es Methoden.

Eine Methode ist ein benannter Codeabschnitt, der über seinen Namen aufgerufen und ausgeführt werden kann. Dabei können einer Methode mit Hilfe von Parametern Daten übergeben werden, außerdem kann eine Methode über einen Rückgabewert verfügen. Parameter dienen also der Eingabe von Daten, der Rückgabewert hingegen der Ausgabe von Daten, wobei beide allerdings optional sind.

Eine einfache Methode verfügt weder über Parameter noch über einen Rückgabewert und bezieht alle Daten, die zu ihrer Ausführung benötigt werden, aus der Klasse, welche die Methode enthält.

Prinzipiell werden Parameter in einer kommagetrennten Liste an die Methode übergeben, die in runden Klammern hinter dem Methodennamen angegeben wird. Werden keine Parameter verwendet, so wird nur ein leeres Paar runder Klammern an den Methodennamen angehängt. Der Rückgabewert wird hingegen vor dem Methodennamen notiert, indem der Typ des Rückgabewertes angegeben wird. Wird kein Rückgabewert verwendet, wird dies mit dem Schlüsselwort void gekennzeichnet. Als Methode ohne Parameter und Rückgabewert wird daher Conjugate eingeführt, welche die Konjugation einer komplexen Zahl berechnet. Die Konjugation ergibt sich, indem das Vorzeichen des Imaginärteils umgekehrt wird, so dass die Konjugation der komplexen Zahl
a + b × i
als
a - b × i
dargestellt wird. Da mathematische Operatoren an dieser Stelle noch nicht behandelt wurden, wird die Methode nur als Platzhalter eingefügt, wobei sie vorerst über keine Funktionalität verfügt.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }

        void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Für die Namensgebung einer Methode gilt, dass der Name mit einem Verb beginnt, dem Substantive folgen können, wobei für die Schreibweise Pascal Case verwendet wird. Auch für eine Methode kann die Sichtbarkeit definiert werden, wobei die gleichen Zugriffsmodifizierer wie bei Feldern zur Verfügung stehen.

In der Regel werden Methoden, die Hilfsaufgaben übernehmen, als private gekennzeichnet. Methoden, welche die Schnittstelle einer Klasse nach außen darstellen, werden mit dem gleichen Zugriffsmodifizierer wie die Klasse gekennzeichnet, also mit internal oder public, je nachdem, ob die Methode nur in der Assembly oder der gesamten Anwendung benötigt wird. Wird kein Zugriffsmodifizierer angegeben, ist eine Methode implizit private.

Methoden werden, da sie die Semantik einer Klasse definieren, ebenfalls mit XML-Kommentaren versehen, wobei wiederum das <summary>-Tag zum Einsatz kommt. Da eine Methode keine Anweisung ist, wird ihre Definition nicht mit einem Semikolon abgeschlossen.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }

        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Wenn ein Rückgabewert für eine Methode benötigt wird, so kann er dadurch definiert werden, dass sein Typ in der Definition der Methode an Stelle von void angegeben wird. Eine Methode, die überprüft, ob so wohl Real- wie auch Imaginärteil dem Zahlenwert Null entsprechen - und damit die gesamte komplexe Zahl der komplexen Null entspricht - gibt entweder true oder false zurück, womit sich als Typ des Rückgabewertes bool ergibt.

Methoden, deren Rückgabewert bool ist, folgen bei der Benennung einer weiteren Richtlinie: Als Verb wird in der Regel is eingesetzt, so dass sich für den Test auf Null der Name IsZero ergibt. Der Grund für diese Richtlinie ist, dass der Name einer solchen Methode als logische Aussage gelesen werden kann.

Außerdem enthalten Methoden, die über einen Rückgabewert verfügen, als letzte Anweisung ein return, so dass in dieser Hinsicht eine gewisse Ähnlichkeit zu get von Eigenschaften besteht.

Sofern eine Methode über einen Rückgabewert verfügt, wird dieser gesondert von <summary> in dem XML-Kommentar der Methode aufgeführt und durch das XML-Tag <returns> gekennzeichnet. Um innerhalb der Dokumentation Schlüsselwörter als solche hervorzuheben, können sie durch das XML-Tag <c> markiert werden.
C#
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
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }

        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Schließlich können Methoden auch Parameter enthalten, mit deren Hilfe Daten an eine Methode bei ihrem Aufruf übergeben werden können. Wie bereits erwähnt, werden Parameter in einer kommaseparierten Liste innerhalb der runden Klammern definiert. Im Gegensatz zum Rückgabewert reicht es allerdings nicht aus, hierbei nur die Typen der Parameter anzugeben, da sie sonst innerhalb der Methode nicht unterscheidbar wären.

Daher erhält jeder Parameter einen Namen, wobei dafür die Richtlinien der Namensgebung von Feldern gelten, mit der Ausnahme, dass für die Schreibweise von Parametern Camel Case verwendet wird. Als Beispiel bieten sich die Addition und die Multiplikation mit einer weiteren komplexen Zahl und die Potenz mit einer reellen Zahl an. Alle drei Methoden verfügen über keinen Rückgabewert, da das Ergebnis direkt in der komplexen Zahl gespeichert wird.

Während den ersten beiden Methoden ein Objekt der Klasse ComplexNumber übergeben wird, erwartet die Potenz eine Dezimalzahl als Parameter. Jeder Parameter wird, wie bereits der Rückgabewert, durch einen entsprechenden XML-Kommentar beschrieben, der durch das <param>-Tag gekennzeichnet wird. Innerhalb des öffnenden Tags befindet sich das Attribut name, dem der Name des beschriebenen Parameters zugewiesen wird.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }

        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current complex
            //          number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to a
            //          power.
            //          2007-04-09
        }

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Obwohl die Definitionen der Methoden Add, Multiply und Pow prinzipiell gleich aussehen, unterscheiden sie sich in einem wesentlichen Aspekt: Die Parameter von Add und Multiply sind vom Typ ComplexNumber - einem Verweistyp -, während der Parameter der Methode Pow vom Typ float ist - einem Wertetyp. Das heißt, dass die Methoden Add und Multiply nur einen Verweis auf ihren jeweiligen Parameter erhalten, die Methode Pow dagegen eine Kopie des Wertes des Parameters.

Verändert eine der Methoden also ihren Parameter, so hat das verschiedene Auswirkungen. Die Methoden Add und Multiply würden nicht nur den Wert ändern, auf den sie zugreifen, sondern auch den Wert der Methode, aus der sie aufgerufen werden, was eventuell nicht gewünscht ist. Die Methode Pow hingegen kann ihren Wert nach Belieben ändern, da sie eine eigene Kopie erhalten und daher keinen Zugriff auf die Daten der aufrufenden Methode hat.

Diese beiden Möglichkeiten, einen Parameter als Verweis oder als echte Kopie der Daten zu übergeben, werden by reference und by value genannt. Wertetypen werden standardmäßig by value übergeben, Verweistypen by reference. Allerdings kann auch ein Wertetyp by reference übergeben werden, so dass die aufrufende und die aufgerufene Methode auf die gleichen Daten zugreifen.

Dies geschieht, indem das Schlüsselwort ref dem Parameter vorangestellt wird, was allerdings in der Praxis nur sehr selten benötigt wird. Andersherum kann ein Verweistyp auch by value übergeben werden, allerdings muss dazu händisch eine Kopie des Objektes angelegt werden, was unter Umständen sehr aufwändig ist.

Gelegentlich kommt es vor, dass mehr als ein Rückgabewert benötigt wird. In der Regel sollte man für diesen Fall eine eigene Datenstruktur entwickeln, welche alle notwendigen Daten aufnehmen kann. Alternativ können Parameter aber auch als zusätzliche Rückgabewerte definiert werden, indem ihnen das Schlüsselwort out vorangestellt wird. Parameter, die als Ausgabeparameter gekennzeichnet werden, werden implizit by reference übergeben.

Um eine Methode aufzurufen, wird zunächst das Objekt, an dem sie aufgerufen werden soll, genannt. Darauf folgt der Operator . und der Name der Methode, gefolgt von runden Klammern. Schließlich wird dieser Aufruf mit einem Semikolon abgeschlossen. Sofern Parameter an die Methode übergeben werden sollen, werden deren Werte innerhalb der runden Klammern kommasepariert angegeben. Sofern eine Methode innerhalb des eigenen Objektes aufgerufen werden soll, entfällt die Angabe des Objektnamens.
C#
1
2
3
4
5
6
7
8
// Conjugate a complex number.
complexNumber.Conjugate();

// Raise it to the power of 2.
complexNumber.Pow(2);

// Raise to the power of 2 from within the current instance.
Pow(2);
Allen Feldern, Eigenschaften und Methoden, die bislang vorgestellt wurden, ist gemein, dass sie objektgebunden sind. Das heißt, sie beziehen sich immer auf ein Objekt, auch wenn sie innerhalb einer Klasse definiert wurden. In der Regel entspricht dies dem gewünschten Verhalten, gelegentlich sollen Felder, Eigenschaften oder Methoden aber klassengebunden sein.

Auf klassengebundene Felder, Eigenschaften und Methoden kann direkt über die Klasse zugegriffen werden, ohne ein bestimmtes Objekt ansprechen zu müssen. Zudem können diese Elemente verwendet werden, ohne dass überhaupt ein Objekt der entsprechenden Klasse erzeugt wurde. Außerdem existiert ein klassengebundenes Element nur ein einziges Mal, unabhängig davon, wie viele Objekte erzeugt wurden - alle Objekte der Klasse teilen sich die einzige Instanz der klassengebundenen Elemente.

Klassengebundene Felder können beispielsweise dazu genutzt werden, um klassenweit gültige Status- oder Konfigurationsdaten allen Objekten der Klasse zur Verfügung zu stellen, ohne dass für jedes Objekt eine eigene Verwaltung dieser Daten bestehen muss. Elemente, die klassengebunden sind, werden in C# als statisch bezeichnet.

Um ein Element als statisch zu kennzeichnen, wird hinter dessen Zugriffsmodifizierer das Schlüsselwort static angegeben. Wenn eine Klasse nur statische Elemente enthält, kann neben den einzelnen Elementen auch die gesamte Klasse als statisch markiert werden, indem hinter ihrem Zugriffsmodifizierer das Schlüsselwort static angegeben wird. Da ein Objekt einer statischen Klasse auf Grund der fehlenden eigenen Felder, Eigenschaften und Methoden sinnlos wäre, kann von einer statischen Klasse kein Objekt erzeugt werden.

Der Aufruf einer statischen Methode erfolgt genauso wie der einer objektgebundenen Methode, mit der Ausnahme, dass nicht das Objekt vorangestellt wird, an dem die Methode aufgerufen werden soll. Statt dessen wird die Klasse angegeben, welche die entsprechende Methode enthält. Sofern eine Methode innerhalb der eigenen Klasse aufgerufen werden soll, entfällt die Angabe des Klassennamens.
C#
1
2
3
4
5
6
7
8
// Call a static method on class Foo.
Foo.Bar();

// Call a static method with parameters.
Foo.Bar("Hello world.");

// Call a static method from within the current class.
Bar();
Eine besondere Rolle in diesem Zusammenhang spielt die statische Methode Main, bei der die Ausführung einer Anwendung startet, weshalb in der gesamten Anwendung nur eine einzige Methode diesen Namens existieren darf. Die Klasse, in der die Methode Main enthalten ist, spielt dabei zunächst keine Rolle, da von ihr kein Objekt erzeugt wird - was wiederum begründet, warum Main eine statische Methode sein muss.

Als Rückgabewert für Main können die Typen void und int angegeben werden, je nachdem, ob ein Rückgabewert benötigt wird. Falls nicht, wird void verwendet, bei Angabe von int kann mit Hilfe der Anweisung return ein Wert zurückgegeben werden, der vom Betriebssystem ausgewertet werden kann. Insbesondere bei Konsolenanwendungen ist dieser Rückgabewert ein häufig genutztes Verfahren, um Fehler in der Anwendung an das Betriebssystem zu melden.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents the application class.
    /// </summary>
    public static class Program
    {
        /// <summary>
        /// Executes the application.
        /// </summary>
        public static void Main()
        {
        }
    }
}
Die Klasse Program verfügt mit der Existenz der statischen Methode Main über alle Voraussetzungen, um in eine ausführbare Assembly mit der Dateiendung .exe übersetzt zu werden. Das Kompilieren erfolgt genauso wie bei einer Assembly, die als Komponente übersetzt wird, außer dass der Parameter an Stelle von /target:library nun /target:exe lautet.

Der Aufruf von
csc /target:exe Program.cs
unter .NET und von
mcs /target:exe Program.cs
unter Mono erzeugen also eine entsprechende Assembly, die ausgeführt werden kann. Unter anderen Betriebssystemen als Windows kann es notwendig sein, die Assembly explizit über die Runtime von Mono zu starten.
mono Program.exe
Da die Klasse Program die Klasse ComplexNumber nutzen können soll, muss sie entsprechenden Zugriff erhalten. Dies geschieht entweder, indem die Klasse ComplexNumber als eigene Komponente übersetzt und anschließend eingebunden wird, oder indem beide Klassen in der gleichen Assembly bereitgestellt werden. Das zweite ist an dieser Stelle deutlich einfacher, weshalb die Anwendung mit
csc /target:exe Program.cs ComplexNumber.cs
unter .NET und mit
mcs /target:exe Program.cs ComplexNumber.cs
unter Mono erneut übersetzt wird. Da die Klasse ComplexNumber inzwischen ein wenig länger geworden ist, bietet es sich an, den Code zu gliedern. Dazu gibt es in C# die Direktive #region, die den Beginn einer Region markiert, die durch eine weitere Direktive - #endregion - abgeschlossen wird. Regionen werden beispielsweise von Visual Studio dazu genutzt, Abschnitte zusammenfassen und zuklappen zu können.

Des weiteren kann eine Region benannt werden, indem hinter der Direktive #region eine Beschreibung angegeben wird. Außerdem können Regionen ineinander verschachtelt werden, um untergeordnete Regionen zu erstellen.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        #region Properties
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to
            //          a power.
            //          2007-04-09
        }
        #endregion

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Die Methoden zur Addition und Multiplikation von komplexen Zahlen haben einen Nachteil, denn sie ermöglichen nur die Addition und Multiplikation von zwei komplexen Zahl. Um allerdings die Summe oder das Produkt aus einer komplexen und einer reellen Zahl zu berechnen, muss die reelle Zahl erst in eine komplexe Zahl abgebildet werden, bei welcher der Realteil der reellen Zahl entspricht, der Imaginärteil hingegen Null ist.

Zur Lösung dieses Problems können die Methoden Add und Multiply mehrfach definiert werden, sofern sich die einzelnen Definitionen in ihrer Signatur unterscheiden. Als Signatur wird dabei der Name einer Methode einschließlich der Typen ihrer Parameter bezeichnet. Der Rückgabewert spielt für die Signatur allerdings keine Rolle, weshalb zwar zwei Methoden mit dem gleichen Rückgabewert und unterschiedlichen Parametern definiert werden können, allerdings nicht umgekehrt.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        #region Properties
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero.
            //          2007-04-09

            // Return the result to the caller.
            return false;
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The real number that is
        /// used as summand.</param>
        public void Add(float summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The real number that is
        /// used as factor.</param>
        public void Multiply(float factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to a
            //          power.
            //          2007-04-09
        }
        #endregion

        // TODO gr: Add code here.
        //          2007-04-08
    }
}
Seit der Version 3.0 von C# gibt es neben partiellen Klassen auch sogenannte partielle Methoden, die ebenfalls mit Hilfe des Schlüsselwortes partial definiert werden. Eine partielle Methode ermöglicht es, das Vorhandensein einer Methode in einer partiellen Klasse zu definieren, ohne die Methode an sich bereitstellen zu müssen. Die partielle Methode kann dann in einem anderen Bestandteil der Klasse implementiert werden, geschieht dies nicht, wird der Aufruf der entsprechenden Methode entfernt.

Das Einsatzgebiet von partiellen Methoden ähnelt dem von partiellen Klassen: Während es mit partiellen Klassen möglich ist, generierten Code von benutzerdefiniertem Code zu trennen, was beispielsweise von den Designern in Visual Studio genutzt wird, ermöglichen partielle Methoden dem Designer, eine Methode zu definieren und bereits zu verwenden, deren Inhalt allerdings vom Entwickler noch implementiert werden muss.

Partielle Methoden müssen zwingend mit dem Zugriffsmodifizierer private gekennzeichnet werden und können nur void als Rückgabetyp haben. Zudem können partielle Methoden nur innerhalb einer partiellen Klasse definiert werden, da sie sonst nicht vom Entwickler ergänzt werden könnten. Zu guter letzt können partielle Methoden so wohl klassen- wie auch instanzbezogen sein und über Parameter verfügen.

Konstruktoren

Nachdem die Klasse ComplexNumber nun sämtliche benötigten Felder, Eigenschaften und Methoden enthält, fehlt zu der Vollendung ihres Rahmens noch eine Methode, die zur Laufzeit der Anwendung ein Objekt dieser Klasse erzeugt und dieses Objekt mit geeigneten Standardwerten initialisiert. Eine solche Methode wird in der objektorientierten Programmierung als Konstruktor bezeichnet.

Prinzipiell trägt ein Konstruktor immer den Namen der Klasse und gleicht abgesehen von einer Ausnahme einer normalen Methode: Ein Konstruktor verfügt im Gegensatz zu allen anderen Methoden nicht über einen Rückgabewert, so dass dessen Angabe schlichtweg entfällt. Parameter hingegen können auch bei Konstruktoren angegeben werden, um beispielsweise Standardwerte für das zu erstellende Objekt vorzugeben.

Wird für eine Klasse kein Konstruktor definiert, verfügt sie implizit über einen parameterlosen Konstruktor, der lediglich dazu dient, ein Objekt dieser Klasse zu erzeugen. Ebenso wie normale Methoden können Konstruktoren überladen und mit einem Zugriffsmodifizierer versehen werden, wobei dieser angibt, von wo aus die Klasse instanziiert werden kann.

In der Regel wird als Zugriffsmodifizierer der der Klasse verwendet, allerdings gibt es Fälle, in denen die Instanziierung verhindert werden soll. Um ein solches Verhalten zu erreichen, kann private als Zugriffsmodifizierer für den Konstruktor angegeben werden, wodurch eine Instanziierung nur noch aus der Klasse selbst erfolgen kann.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        #region Properties
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current complex
            //          number.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The real number that is
        /// used as summand.</param>
        public void Add(float summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The real number that is
        /// used as factor.</param>
        public void Multiply(float factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to a
            //          power.
            //          2007-04-09
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using default values.
        /// </summary>
        public ComplexNumber()
        {
            // TODO gr: Set default values for the real and
            //          imaginary part.
            //          2007-04-25
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real value.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        public ComplexNumber(float realPart)
        {
            // TODO gr: Set default values for the real and
            //          imaginary part.
            //          2007-04-25
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real and imaginary
        /// values.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        /// <param name="imaginaryPart">The imaginary
        /// part.</param>
        public ComplexNumber(
            float realPart, float imaginaryPart)
        {
            // TODO gr: Set default values for the real and
            //          imaginary part.
            //          2007-04-25
        }
        #endregion
    }
}
Das Setzen der Werte, die in den Parametern übergeben wurden, erfolgt prinzipiell genauso wie in Eigenschaften. Der einzige Unterschied besteht darin, dass jeder Parameter einen eigenen Namen trägt und nicht über das Schlüsselwort value angesprochen wird.

Dabei besteht die Möglichkeit, den zu setzenden Wert dem Feld oder der Eigenschaft zuzuweisen. In der Regel ist es gleich, welche Variante genutzt wird, allerdings sollte die gewählte Variante durchgängig verwendet werden.

Unter Umständen kann es zu Namenskonflikten kommen, wenn beispielsweise ein Feld oder eine Eigenschaft den gleichen Namen trägt wie ein Parameter. Obwohl solche Konflikte nicht nur in Konstruktoren, sondern grundsätzlich in jeder Methode auftreten können, häufen sie sich in jenen. Schließlich existiert hier potenziell für jedes Feld ein entsprechender gleichnamiger Parameter.

Um in diesem Fall den Parameter auf der einen und das Feld oder die Eigenschaft auf der anderen Seite unterscheiden zu können, enthält C# das Schlüsselwort this, das eine Referenz auf das eigene Objekt zur Verfügung stellt. Im Konfliktfall muss daher jedem nicht eindeutigen Bezeichner, der ein Feld oder eine Eigenschaft beschreibt, this vorangestellt werden.

Auch wenn die Verwendung des Schlüsselwortes this ansonsten optional ist, gilt es als guter Stil, es bei jedem Verweis auf ein Feld, eine Eigenschaft oder eine Methode des eigenen Objektes anzugeben.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        #region Properties
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current complex
            //          number.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The real number that is
        /// used as summand.</param>
        public void Add(float summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The real number that is used
        /// as factor.</param>
        public void Multiply(float factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to a
            //          power.
            //          2007-04-09
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using default values.
        /// </summary>
        public ComplexNumber()
        {
            // Set default values for the real and
            // imaginary part.
            this.RealPart = 0;
            this.ImaginaryPart = 0;
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real value.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        public ComplexNumber(float realPart)
        {
            // Set default values for the real and
            // imaginary part.
            this.RealPart = realPart;
            this.ImaginaryPart = 0;
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real and imaginary
        /// values.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        /// <param name="imaginaryPart">The imaginary
        /// part.</param>
        public ComplexNumber(
            float realPart, float imaginaryPart)
        {
            // Set default values for the real and
            // imaginary part.
            this.RealPart = realPart;
            this.ImaginaryPart = imaginaryPart;
        }
        #endregion
    }
}
Ein unschöner Aspekt überladener Konstruktoren ist, dass sie unter Umständen redundanten Code enthalten. Daher können Konstruktoren andere Konstruktoren aufrufen, so dass sämtliche gemeinsam genutzte Funktionalität nur in einem Konstruktor enthalten sein muss. Der Aufruf erfolgt, indem hinter der Parameterliste durch einen Doppelpunkt getrennt das Schlüsselwort this mit den entsprechenden Parametern angegeben wird.

Jeder Konstruktor kann zusätzlich eigene Anweisungen enthalten, wobei diese erst dann ausgeführt werden, wenn sämtliche anderen Konstruktoraufrufe abgeschlossen sind. Wird ein Feld so wohl direkt wie auch im Konstruktor mit einem Wert versehen, so überschreibt die Zuweisung im Konstruktor die direkte Zuweisung.
C#
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using System;

namespace GoloRoden.GuideToCSharp
{
    /// <summary>
    /// Represents a complex number.
    /// </summary>
    public class ComplexNumber
    {
        #region Properties
        /// <summary>
        /// Gets or sets the real part.
        /// </summary>
        /// <value>The real part.</value>
        public float RealPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the imaginary part.
        /// </summary>
        /// <value>The imaginary part.</value>
        public float ImginaryPart
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the absolute value.
        /// </summary>
        /// <value>The absolute value.</value>
        public float AbsoluteValue
        {
            get
            {
                // TODO gr: Calculate the absolute value
                //          and return it to the caller.
                //          2007-04-08
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Calculates the conjugation.
        /// </summary>
        public void Conjugate()
        {
            // TODO gr: Calculate the conjugation.
            //          2007-04-09
        }

        /// <summary>
        /// Checks whether the complex number is zero.
        /// </summary>
        /// <returns><c>true</c> if the real and the
        /// imaginary part are zero; <c>false</c>
        /// otherwise.<returns>
        public bool IsZero()
        {
            // TODO gr: Check whether the real and the
            //          imaginary part are zero and return
            //          the result to the caller.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The complex number that is
        /// used as summand.</param>
        public void Add(ComplexNumber summand)
        {
            // TODO gr: Add the summand to the current complex
            //          number.
            //          2007-04-09
        }

        /// <summary>
        /// Adds the specified summand to the current complex
        /// number.
        /// </summary>
        /// <param name="summand">The real number that is
        /// used as summand.</param>
        public void Add(float summand)
        {
            // TODO gr: Add the summand to the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The complex number that is
        /// used as factor.</param>
        public void Multiply(ComplexNumber factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-09
        }

        /// <summary>
        /// Multiplies the current complex number with the
        /// specified factor.
        /// </summary>
        /// <param name="factor">The real number that is
        /// used as factor.</param>
        public void Multiply(float factor)
        {
            // TODO gr: Multiply the factor with the current
            //          complex number.
            //          2007-04-10
        }

        /// <summary>
        /// Raises the current complex number to the power of
        /// the specified real number.
        /// </summary>
        /// <param name="exponent">The real number that is
        /// used as exponent.</param>
        public void Pow(float exponent)
        {
            // TODO gr: Raise the current complex number to a
            //          power.
            //          2007-04-09
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using default values.
        /// </summary>
        public ComplexNumber()
            : this(0, 0)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real value.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        public ComplexNumber(float realPart)
            : this(realPart, 0)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ComplexNumber
        /// type using the specified real and imaginary
        /// values.
        /// </summary>
        /// <param name="realPart">The real part.</param>
        /// <param name="imaginaryPart">The imaginary
        /// part.</param>
        public ComplexNumber(
            float realPart, float imaginaryPart)
        {
            // Set default values for the real and
            // imaginary part.
            this.RealPart = realPart;
            this.ImaginaryPart = imaginaryPart;
        }
        #endregion
    }
}
Im Zusammenhang mit Konstruktoren verfügen Felder zudem über eine Besonderheit - um Felder als konstant zu definieren, kann an Stelle des Schlüsselwortes const das Schlüsselwort readonly verwendet werden. Wird readonly der Definition eines Feldes vorangestellt, kann dessen Wert wie bei const nur direkt und zusätzlich noch im Konstruktor gesetzt werden. Alle weiteren Zugriffe danach können aber nur noch lesend stattfinden.

Konstruktoren können jedoch nicht nur dazu genutzt werden, um Objekte zu initialisieren. Gelegentlich kann es notwendig sein, eine Klasse an sich zu initialisieren, wobei dies insbesondere bei der Verwendung statischer Felder, Eigenschaften oder Methoden vorkommt. Dazu dienen statische Konstruktoren, die auch als Klassenkonstruktoren bezeichnet werden, und sich von den übrigen Konstruktoren durch das zusätzliche Schlüsselwort static unterscheiden.

Außerdem können statische Konstruktoren weder überladen noch parametrisiert werden, zudem verfügen sie nicht über einen Zugriffsmodifizierer. Ausgeführt werden statische Konstruktoren beim ersten Zugriff auf die Klasse - unabhängig von der Art des Zugriffs.

Strukturen

Neben Klassen verfügt C# über ein weiteres Konzept zur Definition von Typen, das Klassen sehr ähnlich ist, nämlich Strukturen. Der wesentliche Unterschied zwischen beiden ist, dass Klassen Verweistypen sind, Strukturen hingegen Wertetypen.

Dementsprechend bietet sich der Einsatz von Strukturen in der Regel dann an, wenn die enthaltenen Felder und Eigenschaften ausschließlich oder zumindest nahezu nur auf Wertetypen basieren.

Häufig werden Strukturen eingesetzt, wenn Daten per COM mit nicht verwalteten Anwendungen ausgetauscht werden, wobei deren Aufbau dann von der über COM angesprochenen Anwendung vorgegeben ist. In diesem Zusammenhang kann mit dem sizeof-Operator der Speicherbedarf einer Struktur in Bytes ermittelt werden, worauf allerdings an dieser Stelle nicht weiter eingegangen wird.

Für Strukturen kann kein parameterloser Konstruktor definiert werden, dieser existiert hingegen implizit immer und initialisiert alle Felder mit den Standardwerten der entsprechenden Typen. Sofern allerdings ein eigener Konstruktor definiert wird, muss dieser zum einen über mindestens einen Parameter verfügen, zum anderen müssen in ihm alle Felder der Struktur mit einem Wert initialisiert werden.

Die Definition einer Struktur erfolgt prinzipiell zu der einer Klasse, außer dass das entsprechende Schlüsselwort struct statt class lautet. In der Entwicklung rein objektorientierter Anwendungen sind Strukturen allerdings verhältnismäßig selten geworden, da in der Regel statt dessen eine Klasse definiert wird, die deutlich mehr Flexibilität bietet.