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 16 Konvertierungen
  gp 16.1 Numerische Typen
  gp 16.2 Konvertierung von Klassen (Verweistypen)
  gp 16.3 Konvertierung von Strukturen (Wertetypen)

Kapitel 16 Konvertierungen

In C# wird zwischen der impliziten und der expliziten Konvertierung unterschieden. Als implizit werden diejenigen Konvertierungen bezeichnet, die immer erfolgreich verlaufen und ohne Datenverlust durchgeführt werden können. Bei numerischen Typen bedeutet dies, dass der Zieltyp den Bereich des Quelltyps vollständig darstellen kann. Ein short kann beispielsweise implizit in einen int konvertiert werden, da der short-Bereich einen Teilsatz des int-Bereichs darstellt.


Galileo Computing

16.1 Numerische Typen  downtop

Für die numerischen Typen sind erweiterte implizite Konvertierungen für alle numerischen Typen mit und ohne Vorzeichen vorhanden. Abbildung 16.1 zeigt die Konvertierungshierarchie. Wenn durchgehende Pfeile von einem Quelltyp zu einem Zieltyp vorhanden sind, ist eine implizite Konvertierung von der Quelle zum Zielort gegeben. Es ist beispielsweise eine implizite Konvertierung von sbyte in short, von byte in decimal und von ushort in long möglich.

Abbildung 16.1  Die C#-Konvertierungshierarchie
Abbildung

Beachten Sie, dass der Pfad von einem Quelltyp zu einem Zieltyp in der Abbildung nicht den Ablauf der Konvertierung widerspiegelt, es wird nur dargestellt, dass eine Konvertierung möglich ist. Mit anderen Worten, die Konvertierung von byte in long erfolgt durch eine einzige Operation, nicht über eine Konvertierung in ushort oder uint.

class Test
{
    public static void Main()
    {
            // implizite Konvertierung
        sbyte v = 55;
        short v2 = v;
        int v3 = v2;
        long v4 = v3;

            // explizite Konvertierung in"kleinere" Typen
        v3 = (int) v4;
        v2 = (short) v3;
        v = (sbyte) v2;
    }
}

Galileo Computing

16.1.1 Konvertierungen und Mitgliedsermittlung  downtop

Bei der Betrachtung überladener Mitglieder muss der Compiler möglicherweise zwischen verschiedenen Funktionen auswählen. Sehen Sie sich folgendes Beispiel an:

using System;
class Conv
{
    public static void Process(sbyte value)
    {
        Console.WriteLine("sbyte {0}", value);
    }
    public static void Process(short value)
    {
        Console.WriteLine("short {0}", value);
    }
    public static void Process(int value)
    {
        Console.WriteLine("int {0}", value);
    }
}
class Test
{
    public static void Main()
    {
        int    value1 = 2;
        sbyte    value2 = 1;
        Conv.Process(value1);
        Conv.Process(value2);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

int 2
sbyte 1

Beim ersten Aufruf von Process() musste der Compiler lediglich den int-Parameter einer der Funktionen zuordnen, und zwar derjenigen mit dem int-Parameter.

Im zweiten Aufruf stehen dem Compiler jedoch drei Versionen zur Auswahl, sbyte, short oder int. Zur Auswahl einer Version wird zunächst versucht, einen exakt übereinstimmenden Typ zu finden. In diesem Fall ist dies sbyte, daher wird diese Version aufgerufen. Wäre die sbyte-Version nicht vorhanden, würde die short-Version ausgewählt, da ein short implizit in einen int konvertiert werden kann. Mit anderen Worten, short befindet sich in der Konvertierungshierarchie »näher« an sbyte und wird daher vorgezogen.

Mit dieser Regel werden viele Fälle abgedeckt, nicht jedoch der folgende:

using System;
class Conv
{
    public static void Process(short value)
    {
        Console.WriteLine("short {0}", value);
    }
    public static void Process(ushort value)
    {
        Console.WriteLine("ushort {0}", value);
    }
}
class Test
{
    public static void Main()
    {
        byte    value = 3;
        Conv.Process(value);
    }
}

Hier verbietet die zuvor angesprochene Regel dem Compiler das Vorziehen einer Funktion, da für ushort und short in beide Richtungen keine implizite Konvertierung möglich ist.

In diesem Fall wird eine andere Regel angewendet: Ist eine einseitige implizite Konvertierung in einen Typ mit Vorzeichen möglich, wird diese allen Konvertierungen in Typen ohne Vorzeichen vorgezogen. Diese Regel wird in Abbildung 16.1 durch die Pfeile mit unterbrochenen Linien veranschaulicht; der Compiler zieht einen Pfeil mit durchgezogener Linie einer beliebigen Anzahl Pfeile mit unterbrochenen Linien vor.

Diese Regel gilt nur in Fällen, in denen eine Einzelpfeilkonvertierung in den Typ mit Vorzeichen möglich ist. Würde die Funktion mit dem short-Typ in int geändert, wäre keine »bessere« Konvertierung möglich, und es würde ein Fehler ausgegeben.


Galileo Computing

16.1.2 Explizite numerische Konvertierungen  downtop

Die explizite Konvertierung – mit Verwendung der cast-Syntax – ist die Konvertierung, die in umgekehrter Richtung zur impliziten Konvertierung durchgeführt wird. Die Konvertierung von short in long ist implizit, die Konvertierung von long in short ist explizit.

Von anderer Seite betrachtet kann eine explizite numerische Konvertierung als Ergebnis einen Wert aufweisen, der sich vom ursprünglichen Wert unterscheidet.

using System;
class Test
{
    public static void Main()
    {
        uint value1 = 312;
        byte value2 = (byte) value1;
        Console.WriteLine("Value2: {0}", value2);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

56

Bei der Konvertierung in byte wird der unwichtigste (niederwertigste) Bestandteil von uint im byte-Wert platziert. In vielen Fällen weiß der Programmierer, dass die Konvertierung entweder erfolgreich verlaufen wird oder von diesem Verhalten abhängt.


Galileo Computing

16.1.3 Geprüfte Konvertierungen  downtop

In anderen Fällen ist es sinnvoll, den Erfolg einer Konvertierung vorab zu prüfen. Dies wird durch Ausführung der Konvertierung in einem geprüften (checked) Kontext erreicht:

using System;
class Test
{
    public static void Main()
    {
        checked
        {
            uint value1 = 312;
            byte value2 = (byte) value1;
            Console.WriteLine("Value: {0}", value2);
        }
    }
}

Wird eine explizite numerische Konvertierung in einem checked-Kontext ausgeführt, dann wird eine Ausnahme erzeugt, sofern der Quellwert nicht in den Zieldatentyp passt.

Die checked-Anweisung erzeugt einen Block, in dem die Konvertierungen vorab auf ihren Erfolg geprüft werden. Ob die Konvertierung geprüft wird oder nicht, entscheidet sich zur Kompilierungszeit. Die checked-Anweisung wird hierbei nicht auf Code in solchen Funktionen angewendet, die vom checked-Block aus aufgerufen werden.

Die Prüfung von Konvertierungen auf deren Erfolg hin führt zu geringen Leistungseinbußen und ist daher für veröffentlichte Software nicht immer geeignet. Es kann jedoch sinnvoll sein, während der Entwicklung der Software alle expliziten numerischen Konvertierungen zu prüfen. Der C#-Compiler stellt die Compileroption /checked bereit, mit der geprüfte Konvertierungen für alle expliziten numerischen Konvertierungen generiert werden. Diese Option kann bei der Softwareentwicklung eingesetzt und zur Leistungsoptimierung bei der veröffentlichten Software deaktiviert werden.

Wenn der Programmierer vom ungeprüften Verhalten abhängt, kann das Aktivieren von /checked zu Problemen führen. In diesem Fall kann mit der unchecked-Anweisung angezeigt werden, dass keine der Konvertierungen in einem Block geprüft werden sollten.

Es ist manchmal hilfreich, den geprüften Status für eine einzelne Anweisung angeben zu können; in diesem Fall kann der checked- oder unchecked-Operator zu Beginn eines Ausdrucks eingefügt werden:

using System;
class Test
{
    public static void Main()
    {
        uint value1 = 312;
        byte value2;

        value2 = unchecked((byte) value1);    // wird nie geprüft
        value2 = (byte) value1;            // wird geprüft, wenn 
/checked aktiviert
        value2 = checked((byte) value1);        // wird immer geprüft
    }
}

In diesem Beispiel wird die erste Konvertierung nie geprüft, die zweite Konvertierung dann, wenn die /checked-Anweisung vorhanden ist, die dritte Konvertierung wird immer geprüft.


Galileo Computing

16.2 Konvertierung von Klassen (Verweistypen)  downtop

Die Konvertierung von Klassen ähnelt der Konvertierung von numerischen Werten, abgesehen davon, dass die Objektkonvertierung im Hinblick auf die Objektvererbungshierarchie und nicht unter Berücksichtigung der numerischen Typenhierarchie erfolgt.

Wie bei der numerischen Konvertierung sind implizite Konvertierungen immer erfolgreich, explizite Konvertierungen können fehlschlagen.


Galileo Computing

16.2.1 Konvertierung in die Basisklasse eines Objekts  downtop

Ein Verweis auf ein Objekt kann implizit in einen Verweis auf die Basisklasse eines Objekts konvertiert werden. Beachten Sie, dass hierbei das Objekt nicht in den Typ der Basisklasse konvertiert wird, der Verweis referenziert lediglich den Basisklassentyp. Das folgende Beispiel veranschaulicht diesen Sachverhalt:

using System;
public class Base
{
    public virtual void WhoAmI()
    {
        Console.WriteLine("Base");
    }
}
public class Derived: Base
{
    public override void WhoAmI()
    {
        Console.WriteLine("Derived");
    }
}
public class Test
{
    public static void Main()
    {
        Derived d = new Derived();
        Base b = d;

        b.WhoAmI();
        Derived d2 = (Derived) b;

        object o = d;
        Derived d3 = (Derived) o;
    }
}

Dieser Code erzeugt die folgende Ausgabe:

Derived

Anfänglich wird eine neue Instanz von Derived erstellt, die Variable d enthält einen Verweis auf dieses Objekt. Der Verweis d wird anschließend in einen Verweis auf den Basistyp Base konvertiert. Das Objekt referenziert beide Variablen, ist jedoch weiterhin vom Typ Derived; dies zeigt sich beim Aufruf der virtuellen Funktion WhoAmI(), da die Version von Derived aufgerufen wird. Es ist ebenfalls möglich, den Base-Verweis b in einen Verweis vom Typ Derived zurückzukonvertieren oder den Derived-Verweis in einen object-Verweis und umgekehrt zu konvertieren.

Die Konvertierung in den Basistyp ist implizit, da (wie in Kapitel 1, Grundlagen der objektorientierten Programmierung, beschrieben) eine abgeleitete Klasse immer eine »Ist Ein(e)«-Beziehung zur Basisklasse aufweist. Mit anderen Worten, Derived »Ist Ein(e)« Base.

Explizite Konvertierungen zwischen Klassen sind möglich, wenn eine »Could-Be«-Beziehung (»Könnte-Sein«-Beziehung) vorliegt. Da Derived von Base abgeleitet ist, könnte es sich bei jedem Verweis auf Base tatsächlich um einen Base-Verweis auf ein Derived-Objekt handeln, daher kann ein Konvertierungsversuch unternommen werden. Zur Laufzeit wird der tatsächliche Typ des Objekts geprüft, auf den durch den Base-Verweis (im Beispiel b) verwiesen wird, um zu ermitteln, ob es sich wirklich um einen Verweis auf Derived handelt. Ist dies nicht der Fall, wird für die Konvertierung eine Ausnahme ausgegeben.

Da es sich bei object um den Basistyp handelt, kann ein beliebiger Verweis auf eine Klasse implizit in einen Verweis auf object konvertiert werden, und ein Verweis auf object kann explizit in einen Verweis auf einen beliebigen Klassentyp konvertiert werden. Abbildung 16.2 veranschaulicht das zuvor genannte Beispiel.

Abbildung 16.2  Unterschiedliche Verweise auf dieselbe Instanz
Abbildung


Galileo Computing

16.2.2 Konvertierung in eine Schnittstelle, die das Objekt implementiert  downtop

Die Schnittstellenimplementierung ähnelt in gewisser Weise der Klassenvererbung. Wenn eine Klasse eine Schnittstelle implementiert, kann eine implizite Konvertierung dazu eingesetzt werden, einen Verweis auf eine Klasseninstanz in einen Schnittstellenverweis zu konvertieren. Diese Konvertierung ist implizit, da zur Kompilierungszeit bekannt ist, dass die Konvertierung erfolgreich sein wird.

Auch hier führt die Konvertierung in eine Schnittstelle nicht zu einer Änderung des zugrunde liegenden Objekttyps. Ein Verweis auf eine Schnittstelle kann also explizit in einen Verweis auf ein Objekt konvertiert werden, das die Schnittstelle implementiert, denn der Schnittstellenverweis könnte (»Could-Be«-Beziehung) eine Instanz des angegebenen Objekts referenzieren.

In der Praxis jedoch wird eine Schnittstelle selten (wenn überhaupt) in ein Objekt zurückkonvertiert.


Galileo Computing

16.2.3 Konvertierung in eine Schnittstelle, die das Objekt möglicherweise implementiert  downtop

Die im vorangegangenen Abschnitt erläuterte implizite Konvertierung von einem Objektverweis in einen Schnittstellenverweis stellt nicht den üblichen Fall dar. Eine Schnittstelle ist besonders in Situationen nützlich, in denen nicht bekannt ist, ob ein Objekt eine Schnittstelle implementiert. Das folgende Beispiel implementiert eine Debugroutine, die eine Schnittstelle verwendet, wenn diese verfügbar ist.

using System;
interface IDebugDump
{
    string DumpObject();
}
class Simple
{
    public Simple(int value)
    {
        this.value = value;
    }
    public override string ToString()
    {
        return(value.ToString());
    }
    int value;
}
class Complicated: IDebugDump
{
    public Complicated(string name)
    {
        this.name = name;
    }
    public override string ToString()
    {
        return(name);
    }
    string IDebugDump.DumpObject()
    {
        return(String.Format(
            "{0}\nLatency: {0}\nRequests: {1}\nFailures: {0}\n",
            new object[] {name,    latency, requestCount, failedCount} 
));
    }
    string name;
    int latency = 0;
    int requestCount = 0;
    int failedCount = 0;
}
class Test
{
    public static void DoConsoleDump(params object[] arr)
    {
        foreach (object o in arr)
        {
            IDebugDump dumper = o as IDebugDump;
            if (dumper != null)
                Console.WriteLine("{0}", dumper.DumpObject());
            else
                Console.WriteLine("{0}", o);
        }
    }
    public static void Main()
    {
        Simple s = new Simple(13);
        Complicated c = new Complicated("Tracking Test");
        DoConsoleDump(s, c);
    }
}

In diesem Beispiel sind Dumpingfunktionen vorhanden, mit denen Objekte und deren interner Status aufgelistet werden kann. Einige Objekte weisen einen komplizierten internen Status auf und erfordern die Rückgabe komplexer Informationen, bei anderen reichen die über ToString() zurückgegebenen Informationen aus.

Dies wird schön durch die IDebugDump-Schnittstelle ausgedrückt, die zum Generieren der Ausgabe eingesetzt wird, wenn eine Implementierung der Schnittstelle vorhanden ist.

In diesem Beispiel wird der as-Operator verwendet, der die Schnittstelle zurückgibt, wenn das Objekt diese implementiert. Andernfalls wird null zurückgegeben.


Galileo Computing

16.2.4 Konvertierung von einem Schnittstellentyp in einen anderen  downtop

Ein Verweis auf eine Schnittstelle kann implizit in einen Verweis auf eine zugrunde liegende Schnittstelle konvertiert werden. Darüber hinaus kann ein Schnittstellenverweis explizit in einen Verweis auf eine beliebige Schnittstelle konvertiert werden, auf der sie nicht basiert. Diese Konvertierung ist nur erfolgreich, wenn es sich bei dem Schnittstellenverweis um einen Verweis auf ein Objekt handelt, das von der anderen Schnittstelle ebenfalls implementiert wird.


Galileo Computing

16.3 Konvertierung von Strukturen (Wertetypen)  toptop

Die einzigen integrierten Konvertierungsvorgänge, die für Strukturen ausgeführt werden können, sind implizite Konvertierungen von einer Struktur in eine Schnittstelle, die dieses Element implementiert. Die Instanz der Struktur wird über das Boxing in einen Verweis umgewandelt und anschließend in den geeigneten Schnittstellenverweis konvertiert. Es gibt keine explizite Konvertierung von einer Schnittstelle in ein struct-Element.






1    Eine Konvertierung von int, uint oder long in float bzw. eine Konvertierung von long in double kann zu einem Genauigkeitsverlust führen, jedoch nicht zu einem Größenverlust.

   

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