Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 7 Weitere Möglichkeiten von C#
Pfeil 7.1 Namensräume (Namespaces)
Pfeil 7.1.1 Zugriff auf Namespaces
Pfeil 7.1.2 Die »using«-Direktive
Pfeil 7.1.3 Globaler Namespace
Pfeil 7.1.4 Vermeiden von Mehrdeutigkeiten
Pfeil 7.1.5 Namespaces festlegen
Pfeil 7.1.6 Der ::-Operator
Pfeil 7.2 Generics – generische Datentypen
Pfeil 7.2.1 Lösung mit einer generischen Klasse
Pfeil 7.2.2 Typparameter mit Constraints einschränken
Pfeil 7.2.3 Generische Methoden
Pfeil 7.2.4 Generics und Vererbung
Pfeil 7.2.5 Konvertierung von Generics
Pfeil 7.2.6 Generische Delegates
Pfeil 7.2.7 Generische Klassen in der .NET-Klassenbibliothek
Pfeil 7.3 Operatorüberladung
Pfeil 7.3.1 Syntax der Operatorüberladung
Pfeil 7.3.2 Operatorüberladungen in »GeometricObjectsSolution«
Pfeil 7.3.3 »true«- und »false«-Operatoren
Pfeil 7.3.4 Benutzerdefinierte Konvertierungen
Pfeil 7.4 Indexer
Pfeil 7.4.1 Überladen von Indexern
Pfeil 7.4.2 Parameterbehaftete Eigenschaften
Pfeil 7.5 Erweiterungsmethoden
Pfeil 7.6 Implizit typisierte Variablen (Typinferenz)
Pfeil 7.7 Lambda-Ausdrücke
Pfeil 7.8 Anonyme Typen
Pfeil 7.9 Nullable-Typen
Pfeil 7.10 Attribute
Pfeil 7.10.1 Das »Flags«-Attribut
Pfeil 7.10.2 Anmerkungen zu den Attributen
Pfeil 7.10.3 Benutzerdefinierte Attribute
Pfeil 7.10.4 Attribute auswerten
Pfeil 7.10.5 Festlegen der Assembly-Eigenschaften
Pfeil 7.11 Partielle Methoden
Pfeil 7.12 Dynamisches Binden
Pfeil 7.12.1 Eine kurze Analyse
Pfeil 7.12.2 Dynamische Objekte
Pfeil 7.13 Unsicherer (unsafe) Programmcode – Zeigertechnik in C#
Pfeil 7.13.1 Einführung
Pfeil 7.13.2 Das Schlüsselwort »unsafe«
Pfeil 7.13.3 Deklaration von Zeigern
Pfeil 7.13.4 Die »fixed«-Anweisung
Pfeil 7.13.5 Zeigerarithmetik
Pfeil 7.13.6 Der Operator »->«


Galileo Computing - Zum Seitenanfang

7.13 Unsicherer (unsafe) Programmcode – Zeigertechnik in C# Zur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

7.13.1 Einführung Zur nächsten ÜberschriftZur vorigen Überschrift

Manchmal ist es erforderlich, auf die Funktionen einer in C geschriebenen herkömmlichen DLL zuzugreifen. Viele C-Funktionen erwarten jedoch Zeiger auf bestimmte Speicheradressen oder geben Zeiger als Aufrufergebnis zurück. Es kann auch vorkommen, dass in einer Anwendung der Zugriff auf Daten erforderlich ist, die sich nicht im Hauptspeicher, sondern beispielsweise im Grafikspeicher befinden. Das Problem ist im ersten Moment, dass C#-Code, der unter der Obhut der Common Language Runtime läuft und als sicherer bzw. verwalteter (managed) Code eingestuft wird, keine Zeiger auf Speicheradressen gestattet.

Ein Entwickler, der mit dieser Einschränkung in seiner Anwendung nicht leben kann, muss unsicheren Code schreiben. Trotz dieser seltsamen Bezeichnung ist unsicherer Code selbstverständlich nicht wirklich »unsicher« oder wenig vertrauenswürdig. Es handelt sich hierbei lediglich um C#-Code, der die Typüberprüfung durch den Compiler einschränkt und den Einsatz von Zeigern und Zeigeroperationen ermöglicht.


Galileo Computing - Zum Seitenanfang

7.13.2 Das Schlüsselwort »unsafe« Zur nächsten ÜberschriftZur vorigen Überschrift

Der Kontext, in dem unsicherer Code gewünscht wird, muss mithilfe des Schlüsselworts unsafe deklariert werden. Es kann eine komplette Klasse oder eine Struktur ebenso als unsicher markiert werden wie eine einzelne Methode. Es ist sogar möglich, innerhalb des Anweisungsblocks einer Methode einen Teilbereich als unsicher zu kennzeichnen.

Ganz allgemein besteht ein nicht sicherer Bereich aus Code, der in geschweiften Klammern eingeschlossen ist und dem das Schlüsselwort unsafe vorangestellt wird. Im folgenden Codefragment wird die Methode Main als unsicher deklariert:


static unsafe void Main(string[] args) {
  // Anweisungen
}

Die Angabe von unsafe ist aber allein noch nicht ausreichend, um unsicheren Code kompilieren zu können. Zusätzlich muss auch noch der Compilerschalter /unsafe gesetzt werden. In Visual Studio 2010 legen Sie diesen Schalter im Projekteigenschaftsfenster unter Erstellen Unsicheren Code zulassen fest. Wenn Sie vergessen, den Compilerschalter einzustellen, wird bei der Kompilierung ein Fehler generiert.


Galileo Computing - Zum Seitenanfang

7.13.3 Deklaration von Zeigern Zur nächsten ÜberschriftZur vorigen Überschrift

In C/C++ sind Zeiger ein klassisches Hilfsmittel der Programmierung, in .NET hingegen nehmen Zeiger eine untergeordnete Rolle ein und werden meist nur in Ausnahmesituationen benutzt. Wir werden daher nicht allzu tief in die Thematik einsteigen und uns auf das Wesentlichste konzentrieren. Wenn Sie keine Erfahrungen mit der Zeigertechnik in C oder in anderen zeigerbehafteten Sprachen gesammelt haben und sich dennoch weiter informieren wollen, sollten Sie C-Literatur zur Hand nehmen.

Zeiger sind Verweise auf Speicherbereiche und werden allgemein wie folgt deklariert:


Datentyp* Variable

Dazu ein Beispiel. Mit der Deklaration


int intVar = 4711;
int* pointer;

erzeugen wir eine int-Variable namens intVar und eine Zeigervariable pointer. pointer ist noch kein Wert zugewiesen und zeigt auf eine Speicheradresse, deren Inhalt als Integer interpretiert wird. Der *-Operator ermöglicht die Deklaration eines typisierten Zeigers und bezieht sich auf den vorangestellten Typ – hier Integer.

Wollen wir dem Zeiger pointer mitteilen, dass er auf die Adresse der Variablen intVar zeigen soll, müssen wir pointer die Adresse von intVar übergeben:


pointer = &intVar;

Der &-Adressoperator liefert eine physikalische Speicheradresse. In der Anweisung wird die Adresse der Variablen intVar ermittelt und dem Zeiger pointer zugewiesen.

Wollen wir den Inhalt der Speicheradresse erfahren, auf die der Zeiger verweist, muss dieser dereferenziert werden:


Console.WriteLine(*pointer);

Das Ergebnis wird 4711 lauten.

Fassen wir den gesamten (unsicheren) Code zusammen. Wenn Sie die Zeigertechnik unter C kennen, werden Sie feststellen, dass es syntaktisch keinen Unterschied gibt:


class Program {
  static unsafe void Main(string[] args) {
    int intVar = 4711;
    int* pointer;
    pointer = &intVar;
    Console.WriteLine(*pointer);
  }
}

C# gibt einen Zeiger nur von einem Wertetyp und niemals von einem Referenztyp zurück. Das gilt jedoch nicht für Arrays und Zeichenfolgen, da Variablen dieses Typs einen Zeiger auf das erste Element bzw. den ersten Buchstaben liefern.


Galileo Computing - Zum Seitenanfang

7.13.4 Die »fixed«-Anweisung Zur nächsten ÜberschriftZur vorigen Überschrift

Während der Ausführung eines Programms werden dem Heap viele Objekte hinzugefügt oder dort aufgegeben. Um eine unnötige Speicherbelegung oder Speicherfragmentierung zu vermeiden, schiebt der Garbage Collector die Objekte hin und her. Auf ein Objekt zu zeigen ist natürlich wertlos, wenn sich seine Adresse unvorhersehbar ändern könnte. Die Lösung dieses Problems bietet die fixed-Anweisung. fixed weist den Garbage Collector an, das Objekt zu »fixieren« – es wird danach nicht mehr verlagert. Da sich dies auf das Verhalten der Laufzeitumgebung auswirken kann, sollten als fixed deklarierte Blöcke nur kurzzeitig benutzt werden.

Hinter der fixed-Anweisung wird in runden Klammern ein Zeiger auf eine verwaltete Variable festgelegt. Diese Variable ist diejenige, die während der Ausführung fixiert wird.


fixed (<Typ>* <pointer> = <Ausdruck>)
{
   ...
}

Ausdruck muss implizit in Typ* konvertierbar sein.

Am besten sind die Wirkungsweise und der Einsatz von fixed anhand eines Beispiels zu verstehen. Sehen Sie sich daher zuerst das folgende Codefragment an:


class Program {
  int intVar; 
  static void Main() {
    Program obj = new Program();
    // unsicherer Code
    unsafe {
      // fixierter Code
      fixed(int* pointer = &obj.intVar) { 
        *pointer = 9;
        System.Console.WriteLine(*pointer);  
      }  
    }  
  }
}

Im Code wird ein Objekt vom Typ Program in Main erzeugt. Es kann grundsätzlich nicht garantiert werden, dass das Program-Objekt obj vom Garbage Collector nicht im Speicher verschoben wird. Da der Zeiger pointer auf das objekteigene Feld intVar verweist, muss sichergestellt sein, dass sich das Objekt bei der Auswertung des Zeigers immer noch an derselben physikalischen Adresse befindet. Die fixed-Anweisung mit der Angabe, worauf pointer zeigt, garantiert, dass die Dereferenzierung an der Konsole das richtige Ergebnis ausgibt.

Beachten Sie, dass in diesem Beispiel nicht die gesamte Methode als unsicher markiert ist, sondern nur der Kontext, in dem der Zeiger eine Rolle spielt.


Galileo Computing - Zum Seitenanfang

7.13.5 Zeigerarithmetik Zur nächsten ÜberschriftZur vorigen Überschrift

Sie können in C# Zeiger so addieren und subtrahieren wie in C oder in anderen Sprachen. Dazu bedient sich der C#-Compiler intern des sizeof-Operators, der die Anzahl der Bytes zurückgibt, die von einer Variablen des angegebenen Typs belegt werden. Addieren Sie beispielsweise zu einem Zeiger vom Typ int* den Wert 1, verweist der Zeiger auf eine Adresse, die um 4 Byte höher liegt, da ein Integer eine Breite von 4 Byte hat.

Im folgenden Beispiel wird ein int-Array initialisiert. Anschließend werden die Inhalte der Array-Elemente nicht wie üblich über ihren Index, sondern mittels Zeigerarithmetik an der Konsole ausgegeben.


class Program {
  unsafe static void Main(string[] args) {
    int[] arr = {10, 72, 333, 4550};
    fixed(int* pointer = arr) {
      Console.WriteLine(*pointer);
      Console.WriteLine(*(pointer + 1));
      Console.WriteLine(*(pointer + 2));
      Console.WriteLine(*(pointer + 3));
    }
  }
}

Ein Array ist den Referenztypen und damit den verwalteten Typen zuzurechnen. Der C#-Compiler erlaubt es aber nicht, außerhalb einer fixed-Anweisung mit einem Zeiger auf einen verwalteten Typ zu zeigen. Mit


fixed(int* pointer = arr)

kommen wir dieser Forderung nach. Das Array arr wird implizit in den Typ int* konvertiert und ist gleichwertig mit folgender Anweisung:


int* pointer = &arr[0]

In der ersten Ausgabeanweisung wird pointer dereferenziert und der Inhalt 10 angezeigt, weil ein Zeiger auf ein Array immer auf das erste Element zeigt. In den folgenden Ausgaben wird die Ausgabeadresse des Zeigers um jeweils eine Integerkapazität erhöht, also um jeweils 4 Byte. Da die Elemente eines Arrays direkt hintereinander im Speicher abgelegt sind, werden der Reihe nach die Zahlen 72, 333 und 4550 auf der Konsole angezeigt.


Galileo Computing - Zum Seitenanfang

7.13.6 Der Operator »->« topZur vorigen Überschrift

Strukturen sind Wertetypen aus mehreren verschiedenen Elementen auf dem Stack und können ebenfalls über Zeiger angesprochen werden. Nehmen wir an, die Struktur Point sei wie folgt definiert:


public struct Point {
  public int X;
  public int Y;
}

Innerhalb eines unsicheren Kontexts können wir uns mit


Point point = new Point();
Point* ptr = &point;

einen Zeiger auf ein Objekt vom Typ Point besorgen. Beabsichtigen wir, das Feld X zu manipulieren und ihm den Wert 150 zuzuweisen, muss der Zeiger ptr zuerst dereferenziert werden. Auf das Ergebnis kann mittels Punktnotation zugegriffen werden, dem die Zahl zugewiesen werden soll. Der gesamte Ausdruck sieht dann wie folgt aus:


(*ptr).X = 150;

C# bietet uns mit dem Operator -> eine einfache Kombination aus Dereferenzierung und Feldzugriff an. Der Ausdruck kann daher gleichwertig auch so formuliert werden:


ptr->X = 150;



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




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


Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern