Rheinwerk Computing <openbook>
Rheinwerk Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 26 Operatorüberladung
  gp 26.1 Unäre Operatoren
  gp 26.2 Binäre Operatoren
  gp 26.3 Ein Beispiel
  gp 26.4 Beschränkungen
  gp 26.5 Richtlinien

Kapitel 26 Operatorüberladung

Durch die Operatorüberladung können Operatoren für Klassen oder Strukturen definiert und mit der Operatorsyntax verwendet werden. Dies ist besonders nützlich bei Datentypen, die gute Definitionen der speziellen Operatorbedeutung aufweisen, sodass der Benutzer wenige Ausdrücke effizient einsetzen kann.

Das Überladen der relationalen Operatoren (==, !=, >, <, >=, <=) wird in Kapitel 27, Freunde finden mit den .NET-Frameworks, im Abschnitt zum Überladen der Equals()-Funktion der .NET-Frameworks näher behandelt.

Das Überladen von Konvertierungsoperatoren wird in Kapitel 24, Benutzerdefinierte Konvertierung, besprochen.


Rheinwerk Computing

26.1 Unäre Operatoren  downtop

Alle unären Operatoren werden als statische Funktionen definiert, die einen einzigen Operator des Klassen- oder Strukturtyps tragen und einen Operator dieses Typs zurückgeben. Folgende Operatoren können überladen werden:

+ – ! ~ ++ -- true false

Die ersten sechs unären überladenen Operatoren werden aufgerufen, wenn die entsprechende Operation für einen Typ aufgerufen wird. Die Operatoren true und false stehen für boolesche Typen zur Verfügung, wenn

if (a == true)

nicht äquivalent zu

if (! (a == false))

ist. Dies geschieht bei SQL-Typen, die einen Nullstatus aufweisen, der weder wahr noch falsch ist. In diesem Fall verwendet der Compiler die überladenen true- und false-Operatoren zum richtigen Auswerten derartiger Anweisungen. Diese Operatoren müssen den Typ bool zurückgeben.

Es gibt keine Möglichkeit, zwischen den im Vorfeld und den nachträglich durchgeführten Wertezuwachs- oder Werteabnahmeoperationen zu unterscheiden. Da es sich bei den Operatoren um statische Funktionen und nicht um Mitgliedsfunktionen handelt, ist diese Unterscheidung nicht wichtig.


Rheinwerk Computing

26.2 Binäre Operatoren  downtop

Alle binären Operatoren tragen zwei Parameter, von denen mindestens einer den Klassen- oder Strukturtyp aufweisen muss, in dem der Operator deklariert wurde. Ein binärer Operator kann einen beliebigen Typ zurückgeben, gibt jedoch typischerweise den Typ der Klasse oder Struktur zurück, in der der Operator definiert wurde.

Folgende binäre Operatoren können überladen werden:

+ – * / % & | ^ << >> (relationale 
Operatoren)

Rheinwerk Computing

26.3 Ein Beispiel  downtop

Die folgende Klasse implementiert einige der überladbaren Operatoren:

using System;
struct RomanNumeral
{
    public RomanNumeral(int value)
    {
        this.value = value;
    }
    public override string ToString()
    {
        return(value.ToString());
    }
    public static RomanNumeral operator -(RomanNumeral roman)
    {
        return(new RomanNumeral(-roman.value));
    }
    public static RomanNumeral operator +(
    RomanNumeral    roman1,
    RomanNumeral    roman2)
    {
        return(new RomanNumeral(
                    roman1.value + roman2.value));
    }

    public static RomanNumeral operator ++(
    RomanNumeral    roman)
    {
        return(new RomanNumeral(roman.value + 1));
    }
    int value;
}
class Test
{
    public static void Main()
    {
        RomanNumeral    roman1 = new RomanNumeral(12);
        RomanNumeral    roman2 = new RomanNumeral(125);

        Console.WriteLine("Increment: {0}", roman1++);
        Console.WriteLine("Addition: {0}", roman1 + roman2);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

Increment: 12
Addition: 138

Rheinwerk Computing

26.4 Beschränkungen  downtop

Es ist nicht möglich, Operatoren für den Mitgliedszugriff, für den Mitgliedsaufruf (Funktionsaufruf) oder die Operatoren +, &&, ||, ?: oder new zu überladen. Dies geschieht im Namen der Einfachheit. Obwohl mit der Überladung lustige Dinge angestellt werden können, wird die Verständlichkeit des Codes doch erheblich herabgesetzt, da der Programmierer stets daran denken muss, dass der Mitgliedsaufruf (beispielsweise) zu besonderen Operationen führen kann. Der Operator new kann nicht überladen werden, da die .NET-Laufzeitumgebung für die Speicherverwaltung zuständig ist, und im C#-Dialekt bedeutet new »Gib mir eine neue Instanz von«.

Es ist darüber hinaus nicht möglich, die zusammengesetzten Zuweisungsoperatoren +=, *= usw. zu überladen, da diese immer in die einfache Operation und eine Zuordnung erweitert werden. Auf diese Weise wird vermieden, dass nur einer der Operatoren definiert wird oder (schauder) dass die Operatoren mit unterschiedlichen Bedeutungen definiert werden.


Rheinwerk Computing

26.5 Richtlinien  toptop

Die Operatorüberladung ist eine Funktion, die nur eingesetzt werden sollte, wenn sie erforderlich ist. Mit »erforderlich« meine ich, dass die Verwendung zu einer Vereinfachung für den Benutzer führt.

Gute Beispiele für die Operatorüberladung sind beispielsweise arithmetische Operationen für komplexe Zahlen- oder Matrizenklassen.

Ein schlechtes Beispiel ist die Definition des Zuwachsoperators (++) für eine string-Klasse mit der Bedeutung »Erhöhe jedes Zeichen in der Zeichenfolge«. Eine gute Richtlinie ist hierbei, nur dann einen Operator zu definieren, wenn ein typischer Benutzer ohne Dokumentation verstehen kann, wofür der Operator verwendet wird. Erfinden Sie keine neuen Bedeutungen für Operatoren.

In der Praxis werden die Operatoren == und != am häufigsten definiert, da ansonsten unerwartete Ergebnisse auftreten können.

Wenn die Typen sich wie integrierte Datentypen verhalten, beispielsweise die BinaryNumeral-Klasse, kann es sinnvoll sein, mehrere Operatoren zu überladen. Beim ersten Hinsehen mag die BinaryNumeral-Klasse wie ein integer erscheinen, der einfach von der Sytsem.Int32-Klasse abgeleitet werden könnte und die Operatoren gratis erhält.

Das funktioniert jedoch aus verschiedenen Gründen nicht. Zunächst können Wertetypen nicht als Basisklassen verwendet werden, und Int32 ist ein Wertetyp. Zweitens, selbst wenn dies möglich wäre, würde es für BinaryNumeral nicht funktionieren, da es sich bei BinaryNumeral nicht um eine Ganzzahl handelt, es wird lediglich ein kleiner Teil des möglichen Wertebereichs unterstützt. Aufgrund dieser Tatsache wäre eine Ableitung keine gute Wahl. Der kleinere Bereich bedeutet, dass selbst bei einer Ableitung von BinaryNumeral von int keine implizite Konvertierung von int in BinaryNumeral vorliegt, alle Ausdrücke würden daher Typumwandlungen per cast erfordern.

Selbst wenn dies nicht so wäre, würde ein solches Vorgehen dennoch keinen Sinn machen, da es bei Datentypen immer darum geht, diese möglichst klein und schlank zu halten, d. h., eine Struktur wäre hier einer Klasse vorzuziehen. Strukturen können natürlich nicht von anderen Objekten abgeleitet werden.






1    Man könnte natürlich damit argumentieren, dass der Mitgliedszugriff anhand von Eigenschaften überladen werden kann.

2    Wie bereits angesprochen, wird bei Verwendung von == für einen Verweistyp (Klasse) ein Vergleich durchgeführt, bei dem geprüft wird, ob die beiden verglichenen Elemente dasselbe Objekt referenzieren – nicht, ob diese über den gleichen Inhalt verfügen. Handelt es sich um einen Wertetyp, wird über == der Inhalt der Wertetypen verglichen, was ausreichend sein kann.

   

Visual C# 2012

Professionell entwickeln mit Visual C# 2012

Windows Presentation Foundation

Schrödinger programmiert C++

C++ Handbuch




Copyright © Rheinwerk Verlag 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.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, fon: 0228.42150.0, fax 0228.42150.77, service@rheinwerk-verlag.de