Share via


Dr. GUI .NET 1.1 #1

 

Überarbeitet für Version 1.1 des Microsoft .NET Framework

13. Mai 2003

Zusammenfassung: Erläutert .NET Framework Klassen und andere Typen und spricht über die Member in Klassen, einschließlich der Eigenschaften. Veranschaulicht außerdem enumerierte Typen, einschließlich Flags und geschachtelten Klassen, und veranschaulicht einige ASP.NET Features: Sitzungsvariablen und Kontrollkästchenlisten sowie AutoPostBack für Textfelder und Kontrollkästchen. (42 gedruckte Seiten)

Inhalte

Einführung und praktische Links
Wo wir waren; Wohin wir gehen
Typen: Die Basiseinheit von .NET
Versuch es doch mal!
Was wir getan haben; Was kommt als nächstes

Weitere Informationen finden Sie in der Quelldatei für diesen Artikel.

Willkommen zurück zu unserem zweiten Artikel. Falls Sie es verpasst haben, sehen Sie sich die erste Installation an, Dr. GUI .NET #0.

Außerdem sollten Sie das .NET-Meldungsboard für die Dr. GUI ausprobieren. Sie können dort besprechen, was Sie hier lesen... und Dr. GUI selbst liest und antwortet auf Ihre Beiträge zu diesen Artikeln (NUR!) jeden Arbeitstag.

Sie können den Dr. GUI-Blog Dr. GUI's Bits and Bytes unter http://blogs.gotdotnet.com/DrGUIansehen.

Die ASP.NET Anwendungen werden tatsächlich auf einem Server ausgeführt: Besuchen Sie die Cold Rooster Consulting-Website , um alle ASP.NET Anwendungen aus dieser Reihe zu sehen.

.NET unterwegs

Wenn Sie ASP.NET verwenden und Web Forms Anwendungen schreiben möchten, die auf mobile Geräte wie Mobiltelefone, RIM Blackberry-Geräte, Palm OS-Geräte, Pocket-PCs und Handheld-PCs ausgerichtet sind, haben Sie Glück: Microsoft® Visual Studio® .NET 2003 enthält jetzt Microsoft® ASP.NET Mobile Controls (früher bekannt als Mobile Internet Toolkit).

ASP.NET Mobile Controls ermöglichen es Ihnen, einen Codesatz für eine Vielzahl von mobilen Geräten zu schreiben (sehen Sie sich die vollständige Liste an) und die Microsoft-.NET Framework sich um die Bereitstellung der richtigen Benutzeroberfläche für eine Vielzahl von Geräten kümmern. Und die Verwendung ist einfach: Wählen Sie einfach einen Projekttyp "ASP.NET Mobile Webanwendung" in Visual Studio .NET aus, um das Projekt zu starten. Beginnen Sie, indem Sie Ihre Sprache auswählen: Microsoft® Visual Basic® .NET, Visual C# ® oder Visual J#®. Arbeiten Sie mit dem projekt ASP.NET Mobile Web Application genauso wie jedes ASP.NET Web Forms Projekt, und stellen Sie es wie üblich bereit. Sie können sogar die ersten Tests Ihrer Anwendungen mit Internet Explorer durchführen!

Zum direkten Ausführen von Microsoft® .NET Framework-Anwendungen auf intelligenten Geräten können Sie .NET Compact Framework verwenden. Hinweis: Sie erhalten dieses Feature nur, wenn Sie Visual Studio .NET 2003 kaufen, nicht die schnäppigen Einzelspracheditionen. Und derzeit nur der Pocket PC und Microsoft® Windows® CE . NET-gestützte Geräte werden unterstützt.

Verwenden von Visual Studio .NET

Dr. GUI freut sich, berichten zu können, dass Microsoft Visual Studio .NET sehr nutzbar ist und es sich lohnt, zu verwenden – insbesondere für den coolen Debugger, die großartigen Features für automatische Vervollständigung und Syntaxüberprüfung sowie die integrierte Hilfe. Und Sie müssen kein Vermögen ausgeben, um eine produktive Entwicklungsumgebung zu erhalten, da Sie Microsoft Visual C# .NET, Visual Basic .NET oder Microsoft Visual C++ .NET Standard Edition für nur 109 US-Dollar (ein unglaubliches Angebot) erhalten können.

Wir haben Visual Studio .NET beim letzten Mal nicht verwendet, sodass Sie ein Gefühl dafür bekommen, wie die .NET Framework und Common Language Runtime funktionieren, aber es ist so einfach und macht so viel Spaß, dass wir es von hier aus verwenden werden.

Wir stellen den Code auf einer zugehörigen HTML-Seite bereit, damit Sie ihn in Visual Studio .NET kopieren oder wie beim letzten Mal selbst ausführen können, wenn Sie das .NET Framework SDK kostenlos herunterladen möchten. (Wenn Sie nur ASP.NET Anwendungen ausführen und intellisense und einen Debugger nicht verwenden, werden wir in einem abschnitt unten die Verwendung der kostenlosen Webmatrix erläutern.) Wenn Sie es sich jedoch leisten können, empfiehlt Dr. GUI dringend, mindestens eine Standard-Edition zu erhalten, damit Sie die Leistungsfähigkeit und Benutzerfreundlichkeit von dem genießen können, was der gute Arzt für die beste Entwicklungsumgebung hält, die überall verfügbar ist.

Ausführen von Konsolenanwendungen mit Visual Studio .NET

Es ist einfach, die Konsolenanwendungen auszuführen, die der gute Arzt hier in Visual Studio .NET bereitstellt. Dies erfordert nur vier einfache Schritte:

  1. Kopieren Sie den Text für das Programm, das Sie ausführen möchten, aus dem Artikel oder der Quelldatei in die Zwischenablage.

  2. Erstellen Sie in Visual Studio .NET ein neues Konsolenanwendungsprojekt in der Sprache Ihrer Wahl. Klicken Sie hierzu im Menü Datei auf Neu, klicken Sie auf Projekt, wählen Sie die Sprache aus, klicken Sie auf das Symbol Konsolenanwendung , und geben Sie einen geeigneten Namen für das Projekt ein.

  3. Drücken Sie in der Quellcodedatei STRG+A , um den gesamten Text auszuwählen, den Visual Studio .NET für Sie geschrieben hat, und fügen Sie dann den Text, den Sie in Schritt 1 kopiert haben, in die Quelldatei ein, wobei der vorherige Inhalt ersetzt wird.

  4. Um ihr Programm zu debuggen, drücken Sie F5 , oder klicken Sie auf Wiedergeben. sie können ohne Debuggen ausgeführt werden, indem Sie STRG+F5 drücken oder auf die Ausrufezeichenschaltfläche (!) klicken. (Wenn Sie das Ausrufezeichen nicht haben, können Sie sie ihrer Symbolleiste hinzufügen.)

    Hinweis Wenn Sie Visual Basic .NET verwenden, erhalten Sie eine Fehlermeldung, dass Main nicht gefunden wurde. Doppelklicken Sie auf die Nachricht, und wählen Sie im angezeigten Dialogfeld das richtige Modul aus.

Wie Sie sehen, ist es einfach, Programme auszuführen.

Wenn der Code bereits in einer separaten Datei enthalten ist, können Sie die folgenden Schritte ausführen:

  1. Erstellen Sie ein neues Projekt in Visual Studio .NET wie in Schritt 2 des obigen Verfahrens zum Ausführen von Konsolenanwendungen.
  2. Um die Quelldatei zu löschen, die Visual Studio .NET für Sie generiert hat, klicken Sie im fenster Projektmappen-Explorer mit der rechten Maustaste auf die Quelldatei, und klicken Sie im Kontextmenü auf Löschen.
  3. Kopieren Sie die Quelldatei in das Projektverzeichnis. Achten Sie darauf, die Datei zu kopieren, anstatt sie zu verschieben. Sie können das Projektverzeichnis für das aktuelle Projekt von Visual Studio finden, indem Sie mit der rechten Maustaste auf das Projekt klicken (NICHT in der Projektmappe– das Projekt ist ein untergeordnetes Element des obersten Projektmappenknotens) und Eigenschaften auswählen oder indem Sie auf das Projekt klicken, während das Eigenschaftenfenster geöffnet ist.
  4. Klicken Sie im Menü Datei auf Vorhandenes Element hinzufügen..., oder klicken Sie mit der rechten Maustaste auf den Projektnamen, und klicken Sie im Kontextmenü auf Vorhandenes Element hinzufügen , um die Datei ihrem Projekt hinzuzufügen.
  5. Führen Sie Ihr Projekt wie in Schritt 4 in der obigen Prozedur zum Ausführen von Konsolenanwendungen aus.

Ausführen ASP.NET Webanwendungen mit Visual Studio .NET

Das Verfahren zum Ausführen einer ASP.NET Webanwendung mit Visual Studio .NET ähnelt ziemlich dem vorherigen Verfahren, außer dass Sie anstelle einer Konsolenanwendung eine ASP.NET Webanwendung erstellen, dem Projekt nur die ASPX-Seite hinzufügen und die Startseite für die Anwendung festlegen müssen.

Beachten Sie, dass von hier aus nur Visual Basic .NET für ASP.NET Anwendungen verwendet wird. So erstellen Sie eine ASP.NET-Webanwendung:

  1. Klicken Sie in Visual Studio .NET im Menü Datei auf Neu, klicken Sie auf Projekt, wählen Sie die Gewünschte Sprache aus, klicken Sie auf das Symbol ASP.NET Webanwendung , und geben Sie ihren Projektnamen ein.
  2. Klicken Sie im fenster Projektmappen-Explorer mit der rechten Maustaste auf die WebForm1.aspx-Standardwebseite, und klicken Sie im Kontextmenü auf Löschen.
  3. Kopieren Sie die Quelldateien in Ihr Projektverzeichnis. Achten Sie darauf, die Dateien zu kopieren, nicht zu verschieben. (Alternativ können Sie eine neue Textdatei im Projektverzeichnis mithilfe von Editor erstellen, dann Text in Editor einfügen und die Datei mit dem richtigen Namen speichern.) Sie benötigen mindestens die ASPX-Datei für die Webseite und die CodeBehind-Datei. In der Regel wird die CodeBehind-Datei wie die ASPX-Datei benannt, wobei die Erweiterung für die Sprache am Ende hinzugefügt wird. Wenn die ASPX-Datei für instance den Namen InstanceSharedASPX.aspx trägt, lautet die CodeBehind-Datei InstanceSharedASPX.aspx.vb.
  4. Klicken Sie im Menü Datei auf Vorhandenes Element hinzufügen..., oder klicken Sie mit der rechten Maustaste auf den Projektnamen, und klicken Sie im Kontextmenü auf Vorhandenes Element hinzufügen... , um ihrem Projekt nur die ASPX-Datei hinzuzufügen. Ändern Sie den Dateityp unten im Dialogfeld in Webdateien .
  5. Klicken Sie im fenster Projektmappen-Explorer mit der rechten Maustaste auf die Seite, die Sie gerade hinzugefügt haben, und klicken Sie im Kontextmenü auf Als Startseite festlegen, um sie zur Startseite der Anwendung zu machen.
  6. Um das Programm zu debuggen, drücken Sie F5 , oder klicken Sie auf die Schaltfläche Wiedergeben , oder drücken Sie STRG+F5 , um es ohne Debuggen auszuführen, oder klicken Sie auf das Ausrufezeichen (!).

Verwenden von Microsoft ASP.NET WebMatrix

WebMatrix ist eine kostenlose IDE, die Sie zum Schreiben ASP.NET Anwendungen verwenden können. Wenn Sie nur die ASP.NET Anwendungen mit diesen Artikeln ausführen möchten, können Sie Webmatrix verwenden. Dr. GUI bevorzugt persönlich die Verwendung von Visual Studio, da nur Visual Studio über IntelliSense, einen Debugger und ein wirklich schnelles Hilfesystem mit einem besseren Index verfügt (WebMatrix verwendet MSDN Online als Hilfesystem). Mit diesen Features ist Visual Studio das Geld wert.

Ein weiteres Problem mit Webmatrix besteht darin, dass sie nicht für die Unterstützung von CodeBehind-Seiten in der gleichen Weise konzipiert ist wie Visual Studio. Daher müssen Sie einige Änderungen an den ASPX-Dateien vornehmen, da sie von Visual Studio erstellt wurden. Im Folgenden werden die Bearbeitungen erläutert.

Schließlich ändert Web Matrix manchmal die HTML-Formatierung auf einige schlechte Weise, sodass Ihre Anwendung möglicherweise nicht genau wie die von Dr. GUI aussieht. Für instance ändert Webmatrix Zeilenumbruchzeichen in Leerzeichen, wo immer sie will. Dies ist normalerweise keine schreckliche Sache, aber es tut es auch mit Text innerhalb eines <PRE></PRE> Tags. Dadurch wird die Darstellung des Texts geändert, da Zeilenumbruchzeichen innerhalb dieses Tags signifikant sind.

Aufgrund dieser Probleme wird Dr. GUI bei Visual Studio bleiben. Aber der Preis für Webmatrix (kostenlos) kann für Sie schwer zu übergeben sein. Wenn Sie also WebMatrix verwenden möchten, um an der Dr. GUI .NET-Serie zu arbeiten, gehen Sie wie folgt vor:

  1. Wenn Sie Kopieren und Einfügen anstelle von Dateien verwenden möchten, erstellen Sie die ASPX-Datei, indem Sie im Menü Datei der Webmatrix auf Neu klicken. Wählen Sie "ASP.NET Seite" aus, legen Sie das Verzeichnis fest, und geben Sie einen Dateinamen ein. Wenn Sie die Dateien kopieren möchten, fahren Sie mit Schritt 4 fort.

  2. Klicken Sie im neuen Dateifenster unten auf die Schaltfläche Alle , und drücken Sie dann STRG+A , um den gesamten vorhandenen Text auszuwählen, und fügen Sie den Text dann an seiner Stelle ein. Speichern Sie diese Datei.

  3. Wiederholen Sie die Schritte 1 und 2 für die Visual Basic-Codebehind-Datei. Wählen Sie in Schritt 1 "Klasse" anstelle von "ASP.NET Seite" aus, und füllen Sie den Rest entsprechend aus. Es spielt keine Rolle, was Sie für Class und Namespace eingeben, da wir den Code sowieso ersetzen werden. Speichern Sie diese Datei, und fahren Sie dann mit Schritt 5 fort.

  4. Wenn Sie Dateien kopieren möchten, kopieren Sie sie einfach in ein beliebiges Verzeichnis, und öffnen Sie die ASPX-Datei in WebMatrix.

  5. Wenn Sie die ASPX-Datei öffnen, müssen Sie unten im Dateifenster auf die Registerkarte Alle klicken, damit der gesamte Code angezeigt wird.

  6. Sie müssen änderungen an beiden Dateien vornehmen. Ändern Sie das <%@ Page ... %> Tag am Anfang der ASPX-Datei wie folgt: Ändern Sie zuerst das CodeBehind="..." -Attribut, um zu lesen Src="...", und löschen Sie dann den Namespace und den Punkt, der dem Namespace folgt, aus dem Wert des Inherits Attributs, aber behalten Sie den Klassennamen bei. Wenn das Tag ursprünglich lautete:

    <%@ Page Language="vb" AutoEventWireup="false" Codebehind="default.aspx.vb" Inherits="SomeASPX.WebForm1" %>
    Sie würden dies in Folgendes ändern:

    <%@ Page Language="vb" autoeventwireup="false" Src="default.aspx.vb" Inherits="WebForm1" %>

  7. Wahrscheinlich müssen Sie die Imports System -Anweisung als erste Zeile der Visual Basic-Datei hinzufügen. Wenn Sie fertig sind, speichern Sie die Datei! (Da Web Matrix überhaupt kein Konzept von Projekten hat, müssen Sie alle Dateien speichern, die Sie nicht direkt ausführen – und Sie können nur ASPX-Dateien direkt ausführen.)

  8. Wechseln Sie zurück zur ASPX-Datei, und führen Sie das Programm aus, indem Sie die Schaltfläche Wiedergabe drücken, und wählen Sie dann aus, ob der ASP.NET Web Matrix-Webserver oder IIS verwendet werden soll. Beachten Sie: Wenn Sie den ASP.NET Web Matrix-Webserver verwenden, stellen Sie sicher, dass Sie ihn beenden, wenn Sie fertig sind.

Wo wir waren; Wohin wir gehen

Beim letzten Mal haben wir darüber gesprochen, was .NET ist – insbesondere über die .NET Framework und Runtime – und warum Sie sich darum kümmern könnten. Anschließend wurde darüber gesprochen, wie Sie das .NET Framework SDK und/oder Visual Studio .NET abrufen und installieren, und einige einfache Programme für Konsolen- und Webseiten wurden sowohl in C# als auch in Visual Basic .NET an der Befehlszeile angezeigt. Schließlich haben wir uns die Metadaten und die Intermediate Language (IL) für diese Programme angesehen und uns ein wenig Zeit mit der Dokumentation beschäftigt. Wenn Sie all dies verpasst haben, können Sie alles darüber im vorherigen Dr. GUI .NET-Artikel lesen.

Dieses Mal werden wir die grundlegende Einheit des .NET Framework: Typen behandeln, einschließlich der integrierten Typen, Klassen und Werttypen.

Typen: Die Basic-Einheit von .NET

Wenn Sie sich die .NET Framework viel angesehen haben, werden Sie feststellen, dass es voll von Typen namens Klassen, Schnittstellen, Enumerationen und Strukturen (auch als Werttypen bezeichnet) ist. Die meisten Typen in .NET sind Klassen. In der Tat müssen Sie in den meisten .NET-Sprachen eine Klasse erstellen, nur um ein "Hello, world!"-Programm zu schreiben, da die Main-Funktion eine Klasse benötigt, deren Member sie sein kann.

Nun ist es richtig, dass der .NET Framework selbst globale Funktionen und Daten unterstützt, und Sie können globale Funktionen in C++ mit dem Managed Extensions for C++ schreiben. Dies ist jedoch nicht unbedingt sprachübergreifend kompatibel, sodass die meisten .NET-Sprachen keine Globalen unterstützen. Weitere Informationen dazu, was Sie sprachenübergreifend tun können und was nicht, finden Sie in der .NET Framework-Dokumentation die Common Language Specification (CLS). Typen und Methoden, die sprachübergreifend verwendet werden können, gelten als "CLS-konform".

Ihre grundlegende Programmieraufgabe beim Programmieren für die .NET Framework besteht jedoch darin, neue Typen (in der Regel neue Klassen) zu erstellen und die Member (meist Methoden mit ein wenig privaten Daten) dieser Klassen zu schreiben.

Klassen

Eine Klasse im .NET Framework ist im Grunde dasselbe wie eine Klasse in jeder objektorientierten Sprache: Sie ist eine Vorlage zum Erstellen von Objekten, die sowohl Daten als auch die Methoden enthalten, die mit diesen Daten arbeiten. Daher wird ein neuer Datentyp definiert, der in Bezug auf die Vorgänge definiert ist, die Sie für ihn ausführen können. Jeder Typ ist eine Abstraktion eines Konzepts. Beachten Sie, dass bei einem guten objektorientierten Entwurf der Typ durch die Gruppe von Vorgängen und nicht durch das Format der internen Daten definiert wird. In der Tat machen Sie die Daten normalerweise privat, sodass andere Teile des Programms die Abstraktion nicht unterbrechen können.

Es ist wichtig, aussagekräftige Abstraktionen mit klaren, aussagekräftigen Vorgängen zu erstellen. Dr. GUI hatte einmal ein ziemliches Argument mit einem Programmierer, der entschied, dass , da "Punkt" und "Größe" beide zwei ganze Zahlen enthielten, "Größe" redundant sein muss, sodass er sie aus der Klassenbibliothek entfernt hat. Dies führte zu Gräueltaten wie "setSize", die einen "Punkt" als Argument und "getSize" einen "Punkt" zurückgibt.

Die Kombination der beiden Typen ist falsch, weil sich die Vorgänge, die Sie an einem Punkt ausführen, z. B. das Verschieben, stark von Vorgängen unterscheiden, die Sie für eine Größe ausführen, z. B. das Aufblasen oder Entflating. Da die Abstraktionen zwei unterschiedliche Gruppen von Vorgängen darstellen, sollten sie zwei unterschiedliche Klassen sein, obwohl ihre Datendarstellung möglicherweise identisch ist.

Abstraktionsbeispiele

Für instance sind die Gleitkommatypen Single und Double (float und double in C#) beide Abstraktionen der Gruppe der reellen Zahlen. Was ist das Datenformat? In der Regel möchten Sie es gar nicht wissen, und Sie sollten es fast nie wissen müssen .

Die Vorgänge, die Single/float und Double/Double unterstützen (Addition, Subtraktion usw.), sind dieselben Vorgänge, die Sie für reelle Zahlen ausführen. Dennoch sind diese Gleitkommatypen im Bereich und in der Genauigkeit begrenzt – sie sind Abstraktionen, nicht die eigentliche Sache.

Beachten Sie, dass Sie keine bitweise Arithmetik (AND/OR/NOT/Shifting) für Gleitkommatypen ausführen können. Diese Vorgänge sind nicht Teil der Abstraktion, sodass sie für Gleitkommatypen nicht unterstützt werden.

Sie können solche Vorgänge jedoch für ganzzahlige Typen wie Int32 (int in C#, Integer in Visual Basic .NET) ausführen, da es sich bei den Ganzzahltypen um Abstraktionen von Computerspeicherwörtern verschiedener Größe handelt. Die Vorgänge, die Sie für sie ausführen können, ähneln dem, was Sie in der Assemblysprache für ein Computerwort ausführen können – nicht nur arithmetische Vorgänge, sondern auch logische Vorgänge. In der Tat könnten Sie einen guten Fall machen, dass diese Typen trotz der Namen int oder Integer (usw.) nicht so sehr eine Abstraktion des mathematischen Satz von ganzen Zahlen sind, als es sich um Abstraktionen von Maschinenwörtern verschiedener Größe handelt.

Sie können komplexere Abstraktionen erstellen: Punkte, Größen, Linien, Rechtecke, Listen, Hashtabellen, Mitarbeiter, Tiere, Fenster, Pinsel, Datenbankverbindungen, Abfragen, XML-Dokumente usw. Für jeden, den Sie erstellen, bestimmen Sie einen geeigneten Satz von Vorgängen für die Abstraktion und ermitteln dann, wie die interne Datendarstellung aussehen muss. Oder wenn Sie Glück haben, hat jemand eine großartige Abstraktion (Typ) erstellt, die Sie verwenden können, anstatt Ihre eigenen zu schreiben.

Auf die gleiche Weise sind typen, die Sie erstellen, häufig Abstraktionen eines Datentyps und, während die Datendarstellung privat bleibt, eine Reihe von Vorgängen verfügbar machen.

Klassen im Vergleich zu Objekten und Operator neu

Wie erstellen Sie also eine Klasse? Sie deklarieren sie an einem Ort (im Gegensatz zu C++, wo Sie normalerweise die Deklaration einer Klasse von ihrer Implementierung trennen), nach der Wortklasse. Beispiel:

C#

   class HelloWorld {
      // ...members go here, discussed next
   }

Visual Basic .NET

   Class HelloWorld
      ' ...members go here, discussed next
   End Class

Dies gibt uns die Gliederung einer Vorlage zum Erstellen von Objekten (wir benötigen mindestens eine Methode oder ein Feld, wie direkt im Voraus beschrieben). Wie erhalten wir bei einer Klasse ein tatsächliches Objekt (oder, anders ausgedrückt, ein instance der Klasse)? Das ist ganz einfach: Normalerweise rufen wir den neuen Operator auf:

C#

   // This is in some method
   HelloWorld hw = new HelloWorld();

Visual Basic .NET

   ' This is in some method
   Dim hw As HelloWorld = New HelloWorld()
   ' or: Dim hw As New HelloWorld()

Nachdem wir dies getan haben, bezieht sich hw auf ein HelloWorld-Objekt , das im Garbage Collection-Heap erstellt wurde, und Sie können mithilfe des hw-Verweises auf jedes seiner Member zugreifen. Beachten Sie, dass die Klammern erforderlich sind. wenn Sie Parameter für die Initialisierung verwenden müssen, können Sie sie in den Klammern übergeben.

Members

Klassen in .NET verfügen über Member. Bei der herkömmlichen objektorientierten Programmierung gibt es zwei Standard Typen von Membern: Datenmember oder Felder; und Funktionsmember oder Methoden (manchmal einschließlich Konstruktoren). Die .NET Framework fügt zwei zusätzliche Typen von Membern hinzu: Eigenschaften und Ereignisse. Und Sie können Typen in Ihrem Typ geschachtelt haben. Wir werden auch kurz über einige spezielle Arten von Mitgliedern in der C#-Sprache sprechen, die im .NET Framework nicht angezeigt werden.

Felder

Felder speichern Daten. Es gibt zwei Typen: instance und statisch (In Visual Basic .NET freigegeben), wobei instance der Standardwert ist. Bei instance Feldern verfügt jedes Objekt dieser Klasse über eine eigene Kopie des Felds– es gibt eine pro instance; daher der Name. Bei statischen/freigegebenen Feldern gibt es nur eine Kopie des Felds, das von allen Instanzen der Klasse freigegeben wird. Wenn also ein Objekt dieses Typs den Wert ändert, ändert es sich für alle Objekte dieses Typs. Hier sehen Sie ein Beispiel für Felder in C# und Visual Basic .NET:

C#

   class Test1 {
      int instanceField; // note we use lower-case first letter...
      static string staticString; // ... because they're private
      // ...we'll show how to access later...
   }

Visual Basic .NET

   Class Test1
      Dim instanceField as Integer ' note we use lower-case first... 
      Shared staticString As String ' ... letter because they're private
      ' We'll show how to access later...
   End Class
      
   

Sie werden feststellen, dass die hier deklarierten Felder (standardmäßig) privat sind. Sie sollten fast nie öffentliche Felder verwenden. Wenn Sie die Felder öffentlich machen, können die Benutzer Ihrer Klasse Ihre Daten ändern, ohne dass Ihre Klasse weiß, was wahrscheinlich Fehler erzeugt. Eigenschaften bieten eine hervorragende Möglichkeit, Benutzern den Komfort der Feldzugriffssyntax mit der Sicherheit und Kapselung von Zugriffsmethoden zu ermöglichen.

Methoden

Eine -Methode ist eine Funktion, die Teil einer Klasse ist. Methoden können auch instance oder statisch sein (Shared in Visual Basic .NET). Auch hier ist instance der Standard, außer in einem Visual Basic .NET-Modul.

Eine instance-Methode funktioniert für eine bestimmte instance eines Objekts. Sie erhält implizit einen Verweis auf das Objekt, an dem sie arbeitet. Sie können implizit auf diesen Verweis zugreifen, indem Sie einfach den Namen des Members (wie in "Bar()") oder explizit mit dem Schlüsselwort (keyword) (Me in Visual Basic .NET) verwenden. Sie können also die Methode Bar aufrufen, indem Sie entweder einfach "Bar()" oder "Me.Bar()" in Visual Basic .NET sagen, /"this. Bar()" in C#.

(Wenn Sie auf ein Feld innerhalb einer Methode in der Klasse zugreifen, in der das Feld deklariert ist, haben Sie die Wahl, diese/Me oder einfach den Feldnamen zu verwenden.)

Sie können auch instance Methoden mit einem Verweis auf ein bestimmtes Objekt aufrufen, wie in SomeObject.Method(). Wenn Sie dies tun, wird der Verweis auf das -Objekt zum this/Me-Verweis in der aufgerufenen Methode.

Statische Methoden erhalten keinen This/Me-Zeiger. Daher können sie nicht auf instance Daten in der -Klasse zugreifen (es sei denn, sie verfügen über einen Verweis auf ein Objekt oder erhalten diese – für instance über einen Parameter zur -Methode). Aus diesem Grund müssen sie mit dem Klassennamen aufgerufen werden, wie in SomeClassName.StaticMethod(). Sie werden häufig für den Zugriff auf statische Daten verwendet. sie werden auch verwendet, um globale Methoden in anderen Systemen zu simulieren.

In der .NET Framework müssen Methoden im Allgemeinen Member einer Klasse sein. (Die Common Language Specification (CLS) erfordert dies für die sprachübergreifende Kompatibilität.) Statische Methoden sind eine Möglichkeit, Ihre Methoden wie globale Methoden zu machen, indem Sie sie aufrufen können, ohne zuerst ein Objekt instanziieren zu müssen, da sie keinen this/Me-Zeiger erfordern. Stattdessen verwenden Sie einfach den Klassennamen und den Methodenaufruf, wie in "System.WriteLine("Hello, World!") oder x = Math.Sin(y)".

Wie wir gesehen haben, ist System.Console.WriteLine ein Beispiel für eine statische Methode (beachten Sie, dass "Console" ein Klassenname ist und dass wir kein Objekt vom Typ Console erstellen mussten, um WriteLine aufzurufen), wie die meisten Methoden in der System.Math-Klasse , z. B . Sin, Cos, Exp usw.

Hier sehen Sie ein Beispiel für einige statische/freigegebene und instance Methoden:

C# (siehe Code)

// compile with: csc InstanceStaticCS.cs
using System;
class Test1Continued {
   int i = 5;
   static int s = 4;
   public void InstanceMethod() {
      Console.WriteLine("InstanceMethod--i: {0}, s: {1}", i, s);
   }
   static public void StaticMethod() {
      // can't access instance data
      // (but could if we created an object as in Main)
      Console.WriteLine("StaticMethod--s: {0}", s);
   }
}
class TestTest1Continued { // a different class
   public static void Main() {
      Test1Continued.StaticMethod();
      Test1Continued tester = new Test1Continued();
      tester.InstanceMethod();
      Console.ReadLine();
   }
}

Visual Basic .NET (siehe Code)

' compile with: vbc InstanceSharedVB.vb
Class Test1Continued
    Dim i As Integer = 5
    Shared s As Integer = 4
    Public Sub InstanceMethod()
        Console.WriteLine("InstanceMethod--i: {0}, s: {1}", i, s)
    End Sub
    Public Shared Sub SharedMethod()
        ' can't access instance data
        ' (but could if we created an object as in Main)
        Console.WriteLine("SharedMethod--s: {0}", s)
    End Sub
End Class
Class TestTest1Continued ' a different class
    Public Shared Sub Main()
        Test1Continued.SharedMethod()
        Dim tester As New Test1Continued()
        tester.InstanceMethod()
        Console.ReadLine()
    End Sub
End Class

Methoden werden standardmäßig privat verwendet, was bedeutet, dass sie nur innerhalb der Klasse aufgerufen werden können, in der sie deklariert werden. Wir deklarieren sie hier öffentlich/öffentlich , damit wir von jeder Klasse aus darauf zugreifen können.

Methoden können überladen werden: Sie können mehrere Methoden mit demselben Namen verwenden, vorausgesetzt, jede methode enthält unterschiedliche Typen in der Parameterliste. Der Compiler (und bei später Bindung die .NET-Runtime) ermittelt anhand der Typen der übergebenen Parameter, welche der Methoden aufgerufen werden sollen. So können Sie Foo() verwenden, das keine Parameter akzeptiert, ein int/Integer akzeptiert und ein int/Integer und ein Double/Double (für instance) akzeptiert und die entsprechende Foo-Methode ohne Parameter, mit int/Integer oder mit einem int/Integer und einem Double/Double aufrufen kann. (Sie können auch mit bestimmten anderen Arten von Parametern aufrufen, da einige Konvertierungen möglich sind. Derzeit möchte Dr. GUI nicht darauf eingehen, zumal die Konvertierungs-/Funktionsüberladungsabgleichsregeln in einigen Fällen ein wenig von Sprache zu Sprache variieren.)

Es gibt eine Reihe anderer Modifizierer für Methoden; Wir werden die meisten davon besprechen, wenn wir über Vererbung sprechen. Dr. GUI informiert Sie jetzt jedoch über extern: Sie wird verwendet, wenn eine Methode in C# deklariert, aber außerhalb des verwalteten .NET Framework implementiert wird, z. B. in einer Microsoft Windows-DLL® mit nativem Code. (In Visual Basic .NET verwenden Sie die Declare-Anweisung oder das DllImport-Attribut, um dasselbe zu tun.) Beachten Sie, dass die Verwendung von vorhandenem nicht verwaltetem Code im .NET Framework einfach ist (vorausgesetzt, Ihr Code verfügt über entsprechende Sicherheitsberechtigungen), anders als in Brand J.

Die ASP.NET Anwendung für dieses Beispiel ist sehr ähnlich, mit der Ausnahme, dass die Console.WriteLine-Ausgabe nie auf die Webseite gelangen würde. Daher müssen wir die Ausgabe in eine Bezeichnung schreiben. Wenn Sie möchten, können Sie diese Anwendung ausführen.

Hier sehen Sie die ASPX-Seite für die Anwendung. Beachten Sie, dass dies mit Visual Studio .NET geschrieben wurde, sodass Tags etwas anders als die eigenständigen Anwendungen verwendet werden, die wir zuletzt geschrieben haben. Dieser Code wird angezeigt.

<%@ Page Language="vb" AutoEventWireup="false" 
            Codebehind="InstanceSharedASPX.aspx.vb" 
               Inherits="InstanceSharedASPX.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
   <HEAD>
      <title>Dr. GUI .NET #1: Instance/Shared Demo</title>
      <meta content="Microsoft Visual Studio.NET 7.0" name="GENERATOR">
      <meta content="Visual Basic 7.0" name="CODE_LANGUAGE">
      <meta content="JavaScript" name="vs_defaultClientScript">
      <meta content="https://schemas.microsoft.com/intellisense/ie5" 
            name="vs_targetSchema">
   </HEAD>
   <body>
      <H1><A href="https://msdn.microsoft.com">Dr. GUI .NET #1</A>: 
            Instance/Shared Demo</H1>
      <FORM id="Form1" method="post" runat="server">
         <P></P>
         <asp:button id="Button1" runat="server" Text="Call Shared 
            method"></asp:button>
         <asp:label id="Label1" runat="server"></asp:label>
         <P></P>
         <asp:button id="Button2" runat="server" Text="Call instance 
            method"></asp:button>
         <asp:label id="Label2" runat="server"></asp:label>
      </FORM>
   </body>
</HTML>

Und hier ist der Code behind (siehe diesen Code):

Imports System.Web.UI.WebControls

Public Class WebForm1
    Inherits System.Web.UI.Page
    Protected WithEvents Label2 As System.Web.UI.WebControls.Label
    Protected WithEvents Button2 As System.Web.UI.WebControls.Button
    Protected WithEvents Label1 As System.Web.UI.WebControls.Label
    Protected WithEvents Button1 As System.Web.UI.WebControls.Button

#Region " Web Form Designer Generated Code "

    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> _
        Private Sub InitializeComponent()

    End Sub

    Private Sub Page_Init(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
        'Put user code to initialize the page here
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
        Test1Continued.SharedMethod(Label1)
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button2.Click
        Dim tester As New Test1Continued()
        tester.InstanceMethod(Label2)
    End Sub
End Class

Class Test1Continued
    Dim i As Integer = 5
      ' Don't use shared fields in ASP.NET; this is for
      ' a demo ONLY!
    Shared s As Integer = 4 ' Don't use Shared in ASP.NET
    Public Sub InstanceMethod(ByRef l As Label)
        'Console.WriteLine("InstanceMethod--i: {0}, s: {1}", i, s)
        l.Text = "InstanceMethod--i: " + i.ToString() + ", s:  " _
            + s.ToString()
    End Sub
    ' Shared methods are OK
    Public Shared Sub SharedMethod(ByVal l As Label)
        ' can't access instance data
        ' (but could if we created an object as in Main)
        l.Text = "SharedMethod--s: " + s.ToString()
    End Sub
End Class

Sie werden feststellen, dass unsere Klasse fast identisch mit der vorherigen Klasse ist, mit der Ausnahme, dass sie durch Festlegen einer Bezeichnung und nicht mithilfe von Console.WriteLine ausgegeben wird. (Console.ReadLine ist noch schlimmer: Es wird Ihre Anwendung hängen bleiben, bis Sie den Browser schließen, da es keine Möglichkeit gibt, die EINGABETASTE zu drücken, da es keine Konsole gibt!) Sie werden sich die Klickhandler der Schaltfläche genau wie beim letzten Mal merken.

Ein wichtiger Hinweis: Es ist zwar in Ordnung , statische/freigegebene Methoden in ASP.NET Anwendungen zu verwenden, sie sollten jedoch KEINE statischen/freigegebenen Felder verwenden. Freigegebene Felder werden auf unvorhersehbare Weise für Instanzen Ihrer ASP.NET-Anwendung freigegeben (also sitzungsübergreifend). Dies führt in der Regel zu einem echten Chaos, also tun Sie es einfach nicht, auch wenn Sie glauben, dass Sie wissen, was Sie tun. Verwenden Sie stattdessen den Ansichts- oder Sitzungszustand, der immer vorhersagbar und zuverlässig funktioniert.

Beachten Sie, dass die Werte von instance Feldern nicht zwischen Ereignishandlern beibehalten werden. Um Werte zu speichern, damit der nächste Handler darauf zugreifen kann, müssen Sie den Sitzungsstatus speichern. Glücklicherweise macht ASP.NET dies extrem einfach – wie wir in der nächsten Anwendung sehen werden.

Erstellen des Objekts: Konstruktoren

Jede Klasse verfügt über mindestens eine Methode, die als Konstruktor bezeichnet wird. Wenn Sie keine Konstruktoren bereitstellen, stellt der Compiler einen für Sie bereit, der keine Parameter akzeptiert und nichts anderes tut, als die Überladung des Konstruktors Ihrer Basisklasse aufzurufen, der keine Parameter akzeptiert.

In C# werden Konstruktoren als Methode mit dem Namen der -Klasse deklariert, aber ohne Rückgabewert. In Visual Basic .NET haben Konstruktoren den Namen New anstelle des Klassennamens.

Konstruktoren können genauso wie jede Methode überladen werden. Jeder Konstruktor ruft einen Konstruktor (standardmäßig den Konstruktor ohne Parameter) in Ihrer Basisklasse auf – in Object , wenn Sie keine explizite Basisklasse haben. Dieser Aufruf erfolgt, bevor ihr Konstruktortext ausgeführt wird. Auf diese Weise wissen Sie, dass Ihre Basisklassen ordnungsgemäß initialisiert werden, bevor der Code Ihres Konstruktors ausgeführt wird.

Der Konstruktor wird nur einmal pro Objekt aufgerufen – und nur zum Zeitpunkt der Erstellung des Objekts. Der primäre Auftrag besteht darin, die instance Felder des Objekts zu initialisieren, damit das Objekt einsatzbereit ist. (Es gibt auch einen statischen Konstruktor, der hauptsächlich zum Initialisieren statischer Felder verwendet wird. Aber lassen Sie uns das vorerst überspringen.)

Bereinigen, wenn Sie fertig sind: "Destruktoren", "IDisposable/Dispose" und "Finalize"

Destruktoren werden von einigen objektorientierten Systemen, insbesondere C++, verwendet, um Objekte zu sauber, bevor sie zerstört werden. Der übliche Zweck eines Destruktors besteht darin, Ressourcen freizugeben, die das Objekt steuert. Häufig sind diese Ressourcen nur Arbeitsspeicher. Da die .NET Framework die automatische Garbage Collection verwendet, ist das Freigeben von verwaltetem Arbeitsspeicher aus einem Destruktor nicht erforderlich. Daher benötigen Sie in der Regel überhaupt keinen Destruktor.

Es ist jedoch auch möglich, dass Ihr Objekt nicht verwaltete Ressourcen wie Datenbankverbindungen, Dateihandles und Fensterhandles steuert. Normalerweise möchten Sie diese nicht verwalteten Ressourcen so schnell wie möglich freigeben, nachdem das Objekt, das die Ressource steuert, nicht mehr benötigt wird. Daher möchten wir eine Möglichkeit haben, genau zu steuern, wann der Code zum Freigeben des nicht verwalteten Codes ausgeführt wird.

Die .NET Framework unterstützt keine C++-Stildestruktoren. Insbesondere unterstützt sie keine deterministische Zerstörung, bei der Sie genau wissen, wann der Destruktor jedes Objekts aufgerufen wird. Die .NET Framework unterstützt jedoch standardmäßige explizite sauber über die Dispose-Methode von IDisposable. Rufen Sie einfach Dispose auf, wenn Sie mit dem Objekt fertig sind, um die nicht verwalteten Ressourcen freizugeben, die das Objekt enthält. Viele Klassen im .NET Framework – z. B. jede Klasse, die ein grafisches Objekt wie ein Fenster oder eine Schriftart, ein Datei- oder Sockethandle oder eine Datenbankverbindung verwendet – implementieren IDisposable. (Sehen Sie sich die vollständige Liste der Klassen im .NET Framework SDK an.)

Es ist in der Regel einfach, Dispose ordnungsgemäß aufzurufen, aber es ist etwas schwieriger, tatsächlich eine richtige Dispose-Methode (und zugehörige Methoden) zu schreiben. Im Entwurfsmuster finden Sie Details zum richtigen Schreiben von Dispose und zugeordneten Methoden für Objekte, die nicht verwaltete Ressourcen steuern.

Sie rufen Dispose häufig auf, aber Sie müssen nur selten Eine eigene Klasse schreiben, die eine nicht verwaltete Ressource steuert und daher die knifflige Implementierung von Dispose und Friends benötigt. Das ist gut so.

Verwenden von

Übrigens unterstützt C# ein Schema, um einen automatischen deterministischen Aufruf eines Dispose bereitzustellen. sie wird als using-Anweisung bezeichnet. Weitere Informationen finden Sie in der Dokumentation zur C#-Sprachspezifikation.

Die using-Anweisung ist in Form eines Try-finally-Blocks definiert, der Ihren Code und einen Aufruf von Dispose enthält. Instance finden Sie wie folgt:

C#

using (SomeType t1 = new SomeType()) {
   t1.SomeMethod();
   // and perhaps other stuff, before or after
}

entspricht genau folgendem:

SomeType t1 = new SomeType();
try {
   t1.SomeMethod();
   // and perhaps other stuff, before or after
}
finally {
   if (t1 != null) ((IDisposable)t1).Dispose();
}

... ohne using-Anweisung geschrieben. Beachten Sie, dass SomeType die System.IDisposable-Schnittstelle (und natürlich die Dispose-Methode ) implementieren muss.

Warnung: Einigen Programmierern ist bekannt, dass sie using-Anweisungen verwenden, um die with-Anweisung in anderen Sprachen als C# zu simulieren. Tun Sie dies nicht – sie sind nicht identisch, da in keiner Sprache eine with-Anweisung automatisch einen Aufruf von Dispose generiert. Die missbräuchliche Verwendung von kann leicht zu schwer zu findenden Fehlern führen, da Sie später versuchen, Objekte zu verwenden, für die bereits Dispose aufgerufen wurde.

Da Visual Basic .NET über keine using-Anweisung verfügt, müssen Sie ihren eigenen Code (ähnlich dem zweiten Codeausschnitt) schreiben, um ordnungsgemäß mit dem Aufrufen von Dispose umzugehen. (Dies ist ein wichtiger Bummer.) Die beiden oben genannten C#-Segmente entsprechen den folgenden in Visual Basic .NET.

Visual Basic .NET

Dim t1 as new SomeType()
Try
   t1.SomeMethod()
   ' and perhaps other stuff, before or after
Finally
   If (Not (t1 is Nothing)) Then CType(t1, IDisposable).Dispose()
End Try

Sie können den Code natürlich vereinfachen, indem Sie die If-Überprüfung entfernen, wenn Sie hundertprozentig sicher sind, dass t1 niemals NULL/Nothing sein wird, und Sie können sogar den Try/End-Wert entfernen, wenn Sie hundertprozentig sicher sind, dass die von Ihnen aufgerufenen Methoden niemals eine Ausnahme auslösen.

Wenn Sie vergessen, Dispose in einer beliebigen Sprache aufzurufen, ist dies nicht das Ende der Welt. Die nicht verwalteten Ressourcen, die vom Objekt verwendet werden, werden schließlich freigegeben, wenn das Objekt mit Müll gesammelt wird. Dies kann jedoch eine ganze Weile lang sein, sodass Sie möglicherweise auf Leistungsprobleme stoßen (nicht um Ressourcenkonflikte zu Erwähnung), wenn Sie den Aufruf Dispose nicht ordnungsgemäß verwenden. Während Sie lernen, sollten Sie sich also angewöhnen, das Richtige zu tun: Wenn Sie mit dem Objekt fertig sind, rufen Sie Dispose für Objekte auf, die es unterstützen.

Finalizer

Die .NET Framework unterstützt auch eine vollständig automatische sauber, indem eine Methode in allen Objekten namens Finalize unterstützt wird, die automatisch aufgerufen wird, bevor das Objekt mit Müll gesammelt wird. (Beachten Sie, dass Sie nicht genau steuern können, wann das Objekt gesammelt wird, sodass Sie nicht genau steuern können, wann Finalize aufgerufen wird. Deshalb haben wir IDisposable.)

Normalerweise müssen Sie keinen Destruktor oder eine Finalize-Methode schreiben, es sei denn, Ihre Klasse steuert eine nicht verwaltete Ressource. Und wenn Ihre Klasse eine nicht verwaltete Ressource steuert, sollten Sie das Richtige tun, indem Sie nicht nur Finalize, sondern auch Dispose schreiben, und zwar nach dem Entwurfsmuster für IDisposable/Dispose/Finalize in der .NET Framework Allgemeine Referenz.

Beachten Sie, dass Sie beim Schreiben einer Finalize-Methode immer die Finalize-Methode Ihrer Basisklasse aufrufen sollten. Aber auch hier sollten Sie selten Finalize-Methoden schreiben, es sei denn, Ihr Objekt muss eine Systemressource freigeben, z. B. ein Handle für ein Fenster oder eine Datei, einen Netzwerksocket oder eine Datenbankverbindung. Die unnötige Verwendung von Finalize verlangsamt das System, da die Runtime mehr Arbeit ausführen und die Garbage Collection verzögern muss. Lassen Sie also den Garbage Collector automatisch alle Objekte verwalten, auf die ihr Objekt verweist. Sie müssen nichts tun, um sie freizugeben.

C# unterstützt einen sogenannten Destruktor mit einer Syntax ähnlich wie C++, aber ohne die deterministische Zerstörungsemantik von C++: Wenn Sie einen Destruktor schreiben, generiert der Compiler einfach eine Finalize-Methode , die automatisch die Finalize-Methode Ihrer Basisklasse aufruft, nachdem er den Code ausgeführt hat, den Sie in der -Methode schreiben (sie müssen also nicht daran denken, den Aufruf der Basisklasse selbst vorzunehmen). Sie können den generierten Code mithilfe von ILDASM auschecken. Beachten Sie, dass Sie eine Finalize-Methode nicht direkt in C# schreiben können. Stattdessen schreiben Sie einen Destruktor, der ihre Finalize-Methode im Hintergrund generiert.

Eigenschaften (NEU!)

Wenn Sie mit der Programmierung in Visual Basic gewohnt sind, sind Sie an Eigenschaften gewöhnt. In der .NET Framework, einschließlich C# und Visual Basic .NET, unterscheiden sich die Eigenschaften ein wenig von der alten Visual Basic für Windows, da sie immer als Get/set-Methoden implementiert werden, nie als Datenfelder. Sie ähneln den Eigenschaften in der Microsoft Visual C++ COM-Erweiterungen in Visual C++ 6.0 sehr. Eigenschaften sind Methoden, die Sie mit der Feldzugriffssyntax aufrufen, um das Beste aus beiden Welten zu bieten: die Sicherheit und Leistungsfähigkeit von Methoden und die sauber Syntax von Feldern.

Grundsätzlich ist die Idee folgendes: Eigenschaften konvertieren Syntax, die wie Feldzugriff aussieht, in einen Methodenaufruf. Sehen Wir uns an, warum dies wichtig ist:

Wenn Sie für instance über eine Klasse namens Person verfügen, die über ein öffentliches Ganzzahlfeld namens Age verfügt, wird wie folgt deklariert (in Visual Basic .NET nur aus Gründen der Kürze...):

Class Person
   Public Age as Integer
   ' and other stuff...
End Class

Dann könnten Ihre C#-Benutzer das Feld mit der folgenden Syntax lesen und festlegen:

Person Jim = new Person(); // create Person object; pointed to by Jim
Jim.Age = 25
int JimsAge = Jim.Age; 
Jim.Age = -5; // invalid, but unchecked if you use a field

Und Ihre Visual Basic .NET-Benutzer würden die folgende Syntax verwenden:

Dim Jim as New Person()   ' create Person object; pointed to by Jim
Jim.Age = 25
Dim JimsAge as Integer = Jim.Age
Jim.Age = -5   ' invalid, but unchecked if you use a field

Damit gibt es zwei Probleme. Zunächst wissen Ihre Benutzer jetzt Details zu Ihrer internen Datendarstellung. Dies kann dazu führen, dass sie sich Freiheiten mit Ihrem Objekt nehmen, indem Sie Dinge tun, die Sie nicht in der Schnittstelle zulassen wollten. Zweitens könnten die Benutzer die Daten in einen ungültigen Wert ändern – möglicherweise zu groß, null oder negativ. Und sie könnten dies tun, ohne dass Ihr Objekt es wusste. Wenn in diesem Fall das Alter auf null festgelegt wurde und wir den IQ durch Dividieren durch Das Alter berechnet haben, erhalten wir eine Division durch null Fehler.

In Sprachen wie C++ kümmern Sie sich um diese Probleme, indem Sie öffentliche Accessormethoden bereitstellen, die wahrscheinlich getAge und SetAge genannt werden. Sie hätten auch ein privates int/Integer-Feld namens age (beachten Sie die Kleinschreibung "a" für private Feldnamen), das von den Accessormethoden gelesen und geschrieben wird. Dies funktioniert, aber die Syntax für den Zugriff auf das Alter ist die viel weniger natürliche und weniger elegante Methodensyntax (hier in C#):

   Jim.SetAge(25);
   int JimsAge = Jim.GetAge();

... und hier in Visual Basic .NET:

   Jim.SetAge(25)
   Dim JimsAge as Integer = Jim.GetAge()

Die Verwendung von Accessormethoden hat jedoch einen enormen Vorteil: Erstens machen Sie Ihre interne Datenstruktur nicht für die Welt verfügbar. Zweitens kann Ihre SetAge-Methode davor schützen, das Alter auf 0 oder auf eine negative Zahl festzulegen. Oder es kann etwas Besonderes tun, wenn sich der Wert des Objekts ändert, z. B. den Bildschirm neu zu streichen, wenn es sich um ein visuelles Objekt handelt, oder ein Ereignis für ein anderes Objekt auslösen. (Für instance könnten Sie ein HappyBirthdayEvent an ein Objekt auslösen, das Kuchen kauft und Geburtstagskarten sendet.)

Eigenschaften: das Beste aus beiden Welten

Eigenschaften ermöglichen Es Ihnen, den Vorteil der eleganteren Syntax des direkten Feldzugriffs zusammen mit der Stabilität und besseren Kapselung von Accessormethoden zu nutzen. Die Syntax zum Implementieren und Verwenden einer Eigenschaft in C# zusammen mit einem Konstruktor wird hier gezeigt (dieser Code wird angezeigt):

// compile with: csc PropertiesCS.cs
using System;
public class Person
{
   int age; // note that this is private; See Note 1
   public Person(int age) 
   { // constructor, See Note 2
      this.age = age; // this. disambiguates!
   }
   public int Age 
   { // property; See Note 1
      get 
      { // See Note 3
         return age;
      }
      set 
      {      // validating value, See Notes 3, 4
         if (value > 0 && value < 150) 
         {
            age = value;
         }
         else 
         {      // throw exception if invalid value
            throw new ArgumentException(
               "Age must be between 1 and 150");
         }
      }
   }
}
class TestPerson 
{
   public static void Main()
   {
      Person Jim = new Person(25); // See Note 5
      Console.WriteLine("This year, Jim was {0} years old", Jim.Age);
      Jim.Age++;      // uses both get and set to do increment, See Note 6
      Console.WriteLine("Next year, Jim will be {0} years old", Jim.Age);
      Console.ReadLine(); // wait before closing
   }
}

Die Syntax in Visual Basic .NET ist ähnlich (siehe diesen Code):

' compile with: vbc PropertiesVB.vb
Imports System
Public Class Person
    Dim ageValue As Integer ' name must change, See Note 1
    Public Sub New(ByVal ageValue As Integer) ' constructor, See Note 2
        Me.ageValue = ageValue ' Me. disambiguates!
    End Sub
    Public Property Age() As Integer ' See Note 1
        Get ' See Note 3
            Return ageValue
        End Get
        Set(ByVal Value As Integer) ' See Note 3
            ' validating value, See Note 4
            If Value > 0 And Value < 150 Then
                ageValue = Value
            Else ' throw exception if invalid value
                Throw New ArgumentException( _
                 "Age must be between 1 and 150")
            End If
        End Set
    End Property
End Class
Class TestPerson
    Public Shared Sub Main()
        Dim Jim As New Person(25) ' See Note 5
        Console.WriteLine("This year, Jim was {0} years old", Jim.Age)
        Jim.Age += 1 ' uses both get and set, See Note 6
        Console.WriteLine("Next year, Jim will be {0} years old", _
               Jim.Age)
        Console.ReadLine()  ' wait before closing
    End Sub
End Class

Beachten Sie folgendes im vorherigen Code:

Hinweis 1: Im C#-Code könnten wir das private Feld alter und die Eigenschaft Age benennen, da bei C# die Groß-/Kleinschreibung beachtet wird. Es ist nicht CLS-kompatibel, Namen nur nach Groß- und Kleinschreibung zu unterscheiden, aber hier ist es in Ordnung, da das Feld privat ist und die CLS-Konformität nur für die nicht privaten Teile der Klasse gilt. Visual Basic .NET berücksichtigt jedoch nicht die Groß-/Kleinschreibung, sodass namen erforderlich sind, die sich nicht nur in der Groß-/Kleinschreibung unterscheiden. Deshalb haben wir "Age" und "ageValue" verwendet.

Hinweis 2: Im Konstruktor (mit dem Namen Person in C#, New in Visual Basic .NET) haben wir den Parameter jedoch genauso wie das private Feld benannt– beide als "age" ("ageValue" in Visual Basic .NET). Wir haben den This/Me-Zeiger verwendet, um zwischen den beiden zu unterscheiden. Dies ist in der .NET-Programmierung von Konstruktoren und Setmethoden in C# und Visual Basic sehr üblich.

Hinweis 3: Die get-Methode der Age-Eigenschaft verwendet den Rückgabewert, um anzugeben, welchen Wert für den Wert der Eigenschaft verwendet werden soll. In C# verwendet die set-Methode für die Age-Eigenschaft den Schlüsselwort (keyword) Wert, um den Wert anzugeben, auf den die Eigenschaft festgelegt wird. In Visual Basic .NET wird der Wert, auf den die Eigenschaft festgelegt wird, explizit als Parameter übergeben.

Hinweis 4: In der set-Methode überprüfen wir den festzulegenden Wert und lösen eine Ausnahme aus, wenn der Wert außerhalb des Bereichs liegt. Unser Aufrufer kann die Ausnahme abfangen, wenn er mag; Wenn dies nicht der Fall ist, wird das Programm beendet. In einer zukünftigen Spalte werden Ausnahmen erläutert.

Hinweis 5: Wir haben new/New verwendet, um ein neues Objekt zu erstellen. Der Konstruktor wird aufgerufen, bevor new/New zurückgegeben wird, wobei das Alter auf 25 initialisiert wird. (Es war 24 in der Version 1.0 des Artikels, aber Jim ist älter.)

Hinweis 6: In C# ruft der Inkrementoperator das Objekt ab und legt es fest. Er ruft den Wert ab, erhöht ihn und legt ihn auf den neuen Wert fest. In Visual Basic .NET gibt es keinen Inkrementoperator, aber wir verwenden die Syntax "X += 1", um die Eigenschaft zu erhöhen.

Während wir auf dem Thema sind, möchte Dr. GUI einige erweiterte Informationen zu Eigenschaften freigeben. Zunächst können Sie schreibgeschützte oder schreibgeschützte Eigenschaften in C# erstellen, indem Sie einfach eine der get - oder set-Methoden weglassen. (Versuchen Sie nicht, beides wegzulassen!) In Visual Basic .NET müssen Sie auch eines der Schlüsselwörter ReadOnly oder WriteOnly verwenden.

Zweitens, da Eigenschaften als Methoden implementiert werden, können sie statisch/Freigegeben oder abstrakt/MustInherit oder virtual/Overridable sein (mehr dazu später). Drittens: Obwohl C# dies nicht unterstützt, können Eigenschaften Parameter enthalten, obwohl wir hier nicht darauf eingehen. Visual Basic .NET unterstützt parametrisierte Eigenschaften. Fügen Sie parameter einfach an den entsprechenden Stellen hinzu, wenn Sie die Eigenschaften definieren und verwenden.

Wenn Sie schließlich die Metadaten für diese Programme überprüfen, sehen Sie, dass die Eigenschaft zusammen mit einigen speziellen Eigenschaftenmetadaten als get_Age - und set_Age-Methoden implementiert ist. Die Tatsache, dass die Methoden feste Namen haben, bedeutet, dass sie mit Sprachen verwendet werden können, die Eigenschaften nicht direkt unterstützen.

Die ASP.NET-Anwendung kann genau den gleichen Personencode verwenden, da sie keine E/A-Vorgänge ausführt. (Wir mussten ein Attribut hinzufügen – siehe unten.) Nachdem Sie das Attribut hinzugefügt haben, können Sie die Person-Klasse sogar in eine separate Datei einfügen und sie bei Bedarf sowohl in ASP.NET- als auch in Konsolenprojekten verwenden, obwohl dies dieses Mal nicht der Fall ist.

Anstatt die gesamte ASPX-Datei für die Webseite zu reproduzieren, sehen wir uns einfach das Formular an (dieser Code wird angezeigt):

    <form id="Form1" method="post" runat="server">
        Jim is now <asp:Label id="Label1" runat="server"> 
        </asp:Label> years old.<p></p>
        <asp:Button id="Button1" runat="server" 
            Text="Birthday time!"></asp:Button>
    </form>

Sie können diese Anwendung ausführen.

Beachten Sie, dass das Bezeichnungssteuerelement in der Mitte des Texts angezeigt wird, sodass wir die Bezeichnung einfach auf die entsprechende Zeichenfolge (Jims aktuelles Alter) festlegen und ASP.NET den Rest erledigen lassen. Das ist viel einfacher als die Zeichenfolgenverkettung, die wir in den anderen Beispielen gemacht haben – das ist die Leistungsfähigkeit von HTML!

Wenn auf die Schaltfläche geklickt wird, wird der Klickhandler im folgenden Code aufgerufen. Sehen wir uns diesen Code an, weniger die Person-Klasse (die genau wie oben angegeben ist) und weniger die von Visual Studio .NET für uns generierten Inhalte (dieser Code wird angezeigt):

Dim Jim As Person

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Jim = ViewState("Jim")
    If Jim Is Nothing Then Jim = New Person(25)
    Label1.Text = Jim.Age.ToString()
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button1.Click
    Jim.Age += 1
    Label1.Text = Jim.Age.ToString()
    ViewState.Add("Jim", Jim)
End Sub

' must add "Serializable" attribute to save state in ASP.NET apps
<Serializable()> _
Public Class Person
   ' ...

Wir mussten nur zwei Methoden schreiben, das Serializable-Attribut zu unserer Person-Klasse hinzufügen und eine Variable deklarieren. Die primäre Funktion der beiden Methoden besteht darin, die Bezeichnung auf den richtigen Wert festzulegen.

Wie bereits erwähnt, werden die Werte von instance Variablen zwischen Ereignissen zerstört. Wenn wir uns also darauf verlassen möchten, dass Jim im Klickhandler der Schaltfläche unterwegs ist, wären wir enttäuscht.

Glücklicherweise macht es ASP.NET fast trivial, Objekte zwischen Ereignissen auf Ihrer Seite zu speichern. Sie können sehen, wie wir dies am Ende des Schaltflächen-Klickhandlers tun: Wir verwenden nur eine Zeile:

ViewState.Add("Jim", Jim)

Dadurch wird der Zustand des Jim-Objekts in den Ansichtszustand für unsere Seite geschrieben. (Wenn sich die Anwendung über mehrere ASPX-Seiten erstreckt, müssten wir den Sitzungszustand anstelle des Ansichtszustands verwenden. Wir könnten den Sitzungszustand auch für diese Single-Page-Anwendung verwenden, aber nicht, weil die Verwendung des Ansichtszustands für Single-Page-Anwendungen, bei denen die Daten klein sind, effizienter ist.) Jeder Benutzer, der die Anwendung ausführt, erhält einen separaten Ansichtszustand, sodass keine Störungen auftreten. Die Zeichenfolge "Jim" ist der Schlüssel zum Abrufen dieses Objekts.

Hinweis: Der Ansichtszustand wird als Text in einem ausgeblendeten Eingabefeld gespeichert, wie wir in Dr. GUI .NET #0 gesehen haben. Neu ist hier, dass wir unsere eigenen Daten zu diesem Ansichtszustand hinzufügen und abrufen können.

Der Prozess der Konvertierung eines Objekts in eine Form, die gespeichert werden kann, wird als "Serialisierung" bezeichnet. Die .NET Framework verfügt über ein umfangreiches, flexibles und erweiterbares Serialisierungsframework, aber es ist nicht ganz automatisch. Mindestens müssen wir dem .NET Framework mitteilen, welche Objekttypen serialisiert werden sollen.

Um den Zustand eines Person Objekts zu speichern, müssen wir nur die Person -Klasse mit dem Serializable -Attribut deklarieren, wie oben gezeigt. Mit diesem Attribut werden den Metadaten der Person Klasse nur einige Metadaten hinzugefügt. Die .NET Framework sieht dies und kümmert sich um den Rest.

Beachten Sie, dass wir nichts anderes tun mussten, um dieses Objekt schreiben zu können. ASP.NET kümmerte sich darum, das Datenformat der Person-Klasse (unter Verwendung der Metadaten) zu ermitteln und das Objekt ordnungsgemäß zu schreiben. Beeindruckend, nicht?

Das Wiederlesen ist fast genauso trivial: Verwenden Sie im Seitenladehandler einfach den folgenden Code:

Jim = ViewState("Jim")
If Jim Is Nothing Then Jim = New Person(25)

Die erste Zeile erstellt das Jim-Objekt aus den Informationen, die im Ansichtszustand gespeichert sind. Auch dies ist alles automatisch. Die Laufzeit hat den richtigen Objekttyp erstellt und aus den Daten initialisiert.

Aber es ist möglich, dass Jim nicht gespeichert wurde – möglicherweise haben wir diese Seite zum ersten Mal besucht, oder vielleicht haben wir nicht auf die Schaltfläche geklickt. (Beachten Sie, dass der Code so optimiert ist, dass der Zustand nur gespeichert wird, wenn er sich vom Standardwert ändert.) Daher müssen wir den Verweis überprüfen, den wir zurückbekommen, um sicherzustellen, dass er nicht Nothing (NULL in C#) ist. Wenn es Nothing ist, müssen wir ein Objekt von Grund auf erstellen und initialisieren.

Events (NEU!)

In Visual Basic für Windows und in COM können Objekte Listener über etwas Interessantes benachrichtigen, indem sie ein Ereignis auslösen. In COM sind Ereignisse sehr kompliziert – zum Glück verbirgt Visual Basic diese Komplikation. Es gibt viele Arten von Komponenten, die Ereignisse auslösen müssen, von Schaltflächen, die Ereignisse auslösen, wenn darauf geklickt wird, bis hin zu Objekten, die Ereignisse auslösen, wenn sich ihre Daten geändert haben.

Das Konzept eines Ereignisses und die Infrastruktur zur Unterstützung von Ereignissen sind in die .NET Framework integriert, sodass das Deklarieren und Auslösen eines Ereignisses einfach und natürlich ist. Wir werden später auf Ereignisse zurückkommen, wenn sie im Fluss unserer Diskussion natürlich vorkommen. Aber vorerst sollten Sie wissen, dass Eigenschaften und Ereignisse, die für die komponentenbasierte Programmierung ziemlich zentral sind, direkt vom .NET Framework unterstützt werden.

Beachten Sie, dass wir bereits erfahren haben, wie .NET Framework Ereignisse in allen unseren ASP.NET-Anwendungen behandelt werden. Jeder der Schaltflächen-Klickhandler verwendet den standardbasierten .NET Framework-Ereignismechanismus. Auch hier werden wir in einer späteren Spalte ausführlich auf Ereignisse eingehen.

Typen und Enumerationen: geschachtelte Klassen und Enumerationen

Sie können auch geschachtelte Klassen und andere Typen als Member eines Typs verwenden – eine Klasse innerhalb einer Klasse, für instance.

Und, geschachtelt oder nicht, Sie können auch eine spezielle Art von Typ haben, die als Enumeration oder Enumeration (Enumeration in Visual Basic .NET) bezeichnet wird. Eine Enumeration ist ein Typ, der statische Konstantenfelder mit bestimmten ganzzahligen Werten enthält. Die Felder sind symbolische Namen für numerische Werte und Bitflags.

Sehen Sie sich diesen C#-Code an, der geschachtelte Klassen und Enumerationen anzeigt (dieser Code wird angezeigt):

// compile with: csc InnerOuterCS.cs
using System;
class Outer {
   public class Nested {
   // if private, couldn't be accessed outside of Outer
      int value;
      public Nested(int value) {
         this.value = value;
      }
      public void Foo() {
         Console.WriteLine("Outer.Nested.Foo: value is {0}", value);
      }
   }
   // The enum could appear outside of a class, too
   public enum ShortDays { Sun = 0, Mon, Tue, Wed, Thu, Fri, Sat };
}
[Flags] public enum BitFlags { Bit0 = 1, Bit1 = 2, Bit2 = 4, Bit3 = 8 };
class TestOuter {
   public static void Main() {
      Outer.Nested n = new Outer.Nested(5);   // create nested object
      n.Foo();                        // call method
      
      // if the enum was outside a class, it'd be ShortDays.Mon, etc.
      Outer.ShortDays monday = Outer.ShortDays.Mon;
      // can write day *AND* short name of day
      Console.WriteLine("Monday is day #{0}, name is {1}", 
         (int)monday, monday.ToString());

      // get integer for today's day: Sunday = 0, Monday = 1, etc.
      int dayNum = (int)DateTime.Today.DayOfWeek; // system property
      // now convert to our ShortDays type
      Outer.ShortDays shortToday = (Outer.ShortDays)dayNum;
      Console.WriteLine("Today is day #{0}, name is {1}", 
         (int)shortToday, shortToday.ToString());

      // Works with Framework's enum DayOfWeek, too
      DayOfWeek longToday = DateTime.Today.DayOfWeek;
      Console.WriteLine("Today is day #{0}, name is {1}", 
         (int)longToday, longToday.ToString());

      BitFlags b = BitFlags.Bit0 | BitFlags.Bit2;
      Console.WriteLine("b's value is {0}, flags are {1}", 
(int)b, b.ToString());
      Console.ReadLine();
   }
}

Und hier ist der Visual Basic .NET-Code für dasselbe Objekt (etwas länger, da jede Enum-Deklaration eine Zeile pro deklariertem Wert benötigt.) (Dieser Code wird angezeigt):

' compile with: vbc InnerOuterVB.vb
Imports System
Class Outer
    Public Class Nested
        ' if private, couldn't be accessed outside of Outer
        Dim value As Integer
        Public Sub New(ByVal value As Integer)
            Me.value = value
        End Sub
        Public Sub Foo()
            Console.WriteLine("Outer.Nested.Foo: value is {0}", value)
        End Sub
    End Class
    ' The enum could appear outside of a class, too
    Public Enum ShortDays
        Sun = 0
        Mon
        Tue
        Wed
        Thu
        Fri
        Sat
    End Enum
End Class

<Flags()> Public Enum BitFlags
    Bit0 = 1
    Bit1 = 2
    Bit2 = 4
    Bit3 = 8
End Enum

Class TestOuter
    Public Shared Sub Main()
        Dim n As New Outer.Nested(5)    ' create nested object
        n.Foo()                         ' call method

        ' if the enum was outside a class, it'd be ShortDays.Mon, etc.
        Dim monday As Outer.ShortDays = Outer.ShortDays.Mon
        ' can write day *AND* short name of day
        Console.WriteLine("Monday is day #{0}, name is {1}", _
         CType(monday, Integer), monday.ToString())

        ' get integer for today's day: Sunday = 0, Monday = 1, etc.
        Dim dayNum As Integer = CType(DateTime.Today.DayOfWeek, Integer)
        ' system property
        ' now convert to our ShortDays type
        Dim shortToday As Outer.ShortDays = CType(dayNum, _
            Outer.ShortDays)
        Console.WriteLine("Today is day #{0}, name is {1}", _
         CType(shortToday, Integer), shortToday.ToString())

        ' Works with Framework's enum DayOfWeek, too
        Dim longToday As DayOfWeek = DateTime.Today.DayOfWeek
        Console.WriteLine("Today is day #{0}, name is {1}", _
           CType(longToday, Integer), longToday.ToString())

        Dim b As BitFlags = BitFlags.Bit0 Or BitFlags.Bit2
        Console.WriteLine("b's value is {0}, flags are {1}", _
            CType(b, Integer), b.ToString())
        Console.ReadLine()
    End Sub
End Class

In beiden Programmen haben wir eine Klasse namens Outer und eine klasse namens Outer.Nested erstellt. Diese geschachtelten Klassen können für die Implementierung von Hilfsobjekten nützlich sein, die hauptsächlich (oder ausschließlich) innerhalb der äußeren Klasse verwendet werden. In diesem Fall haben wir die geschachtelte Klasse öffentlich gemacht, damit sie sowohl außerhalb der äußeren als auch innerhalb der Klasse verwendet werden kann.

Außerdem haben wir einen enumerierten Typ namens Outer.ShortDays mit sieben Werten (0-6) für die Wochentage erstellt. Dies hätte außerhalb der -Klasse erstellt werden können– wenn wir dies getan hätten, würde ihr Name nur ShortDays sein.

In Main haben wir eine Variable dieses Typs namens monday erstellt und sie mit dem konstanten Wert Outer.ShortDays.Mon initialisiert. Dann haben wir den numerischen Wert ausgegeben (indem wir ihn in int/Integer umwandeln und dessen Namen ausgegeben haben.

Wir können auch eine Zeichenfolge abrufen, die ihren Namen enthält, indem Wir die Format-Methode verwenden oder mit Parse aus einer Zeichenfolge konvertieren, die den Namen in den ganzzahligen Wert des Enumerators enthält. Wir können sogar alle Werte und Namen mit GetValues und GetNames abrufen. (Enumeratoren in vielen Systemen haben nicht die Möglichkeit, Ihnen ihre Namen mitzuteilen. Da im .NET Framework Sie metadaten für jeden Typ haben, ist es möglich, sowohl mit den Namen als auch mit den Werten zu arbeiten.)

Sie werden feststellen, dass Sie (mit CType in Visual Basic) umwandeln müssen, um von einem Enumerationstyp in einen Ganzzahltyp zu konvertieren. Normalerweise möchten Sie nicht in und aus ganzzahligen Zahlen konvertieren. Dies wird hier NUR ausgeführt, um zu zeigen, dass Dies möglich ist.

Schließlich haben wir eine Enumeration erstellt, die eine Reihe von Bitflags mit dem Namen BitFlags enthält. Das [Flags]/<Flags()>-Attribut vor der Deklaration bewirkt, dass C# und Visual Basic .NET diesen Typ unterschiedlich behandeln. Für instance ist der Operator "oder" normalerweise nicht für Enumerationen zulässig. Sie können sehen, wie wir bitweise Arithmetik mit ihnen ausführen und sogar die Werte in einer Zeichenfolge abrufen können.

Der ASP.NET Code ist sehr ähnlich, und dieses Mal benötigen wir keinen Sitzungszustand.

Sie können diese Anwendung ausführen.

Die einzige Änderung am Code für die geschachtelte Klasse und Enums besteht darin, die Funktion zu ändern, die Console.WriteLine aufgerufen hat, um stattdessen eine Zeichenfolge mithilfe von String.Format zurückzugeben.

     Public Function Foo() As String
         Return String.Format("Outer.Nested.Foo: value is {0}", value)
     End Function

Wir haben sub in eine Funktion geändert, die eine Zeichenfolge zurückgibt, und die static/SharedString.Format-Methode verwendet, um die Ausgabe zu formatieren. Beachten Sie, dass String.Format in vielen (wenn nicht allen) Fällen mit GENAU denselben Parametern wie Console.WriteLine aufgerufen werden kann, sodass das Ändern des Codes äußerst einfach ist!

Anschließend haben wir ein Formular mit vier Schaltflächen und vier Bezeichnungen geschrieben, die den ersten vier Teilen von Main entsprechen, die die Ausgabe erzeugt haben, und jeden Teil der Ausgabe in die Click-Handlermethode der entsprechenden Schaltfläche eingefügt, wobei erneut Console.WriteLine-Anweisungen so geändert werden, dass stattdessen String.Format aufgerufen wird und die Text-Eigenschaft der entsprechenden Bezeichnung auf die resultierende formatierte Zeichenfolge festgelegt wird. (Der gesamte Formularcode wird angezeigt.)

Wir haben auch einige Textfelder hinzugefügt, um Eingaben für die Konvertierung zwischen ShortDay-Enumerationswerten und -namen zu akzeptieren, sowie ein Textfeld und eine Kontrollkästchenliste, um Eingaben zu akzeptieren, die zwischen dem Dezimalwert, den Bits und der BitFlags-Enumeration konvertiert werden, damit wir den Wert drucken können.

Hier ist der Code für die Ereignishandler, damit Sie sehen können (Sie können den gesamten Code sehen):

Private Sub Button1_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button1.Click
    Dim n As New Outer.Nested(5)    ' create nested object
    Label1.Text = n.Foo()                         ' call method
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button2.Click
    ' if the enum was outside a class, it'd be ShortDays.Mon, etc.
    Dim monday As Outer.ShortDays = Outer.ShortDays.Mon
    ' can write day *AND* short name of day
    Label2.Text = String.Format("Monday is day #{0}, name is {1}", _
        CType(monday, Integer), monday.ToString())
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button3.Click
    ' get integer for today's day: Sunday = 0, Monday = 1, etc.
    Dim dayNum As Integer = CType(DateTime.Today.DayOfWeek, Integer)
    ' system property
    ' now convert to our ShortDays type
    Dim shortToday As Outer.ShortDays = CType(dayNum, Outer.ShortDays)
    Label3.Text = String.Format("Today is day #{0}, name is {1}", _
        CType(shortToday, Integer), shortToday.ToString())
End Sub
Private Sub Button4_Click(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles Button4.Click
    ' Works with Framework's enum DayOfWeek, too
    Dim longToday As DayOfWeek = DateTime.Today.DayOfWeek
    Label4.Text = String.Format("Today is day #{0}, name is {1}", _
       CType(longToday, Integer), longToday.ToString())
End Sub
Private Sub TextBox1_TextChanged(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles TextBox1.TextChanged
    Dim dayString As String = TextBox1.Text
    Try
        Dim day As Outer.ShortDays = _
            System.Enum.Parse(GetType(Outer.ShortDays), dayString, True)
        TextBox2.Text = CType(day, Integer)
    Catch
        TextBox2.Text = "Invalid Short Name"
    End Try
End Sub
Private Sub TextBox2_TextChanged(ByVal sender As System.Object, 
            ByVal e As System.EventArgs) Handles TextBox2.TextChanged
    Dim dayNum As Integer = Val(TextBox2.Text)
    If System.Enum.IsDefined(GetType(Outer.ShortDays), dayNum) Then
        Dim day As Outer.ShortDays = CType(dayNum, Outer.ShortDays)
        TextBox1.Text = day.ToString()
    Else
        TextBox1.Text = "Invalid Enum value"
    End If
End Sub
Private Sub TextBox3_TextChanged(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles TextBox3.TextChanged
    Dim bits As Integer = Val(TextBox3.Text)
    If bits >= 0 And bits <= 15 Then
        Dim b As BitFlags = CType(bits, BitFlags)
        Label5.Text = "'" + b.ToString() + "'"
        ' checkbox indices are reverse order of bits :(
        CheckBoxList1.Items(3).Selected = b And BitFlags.Bit0
        CheckBoxList1.Items(2).Selected = b And BitFlags.Bit1
        CheckBoxList1.Items(1).Selected = b And BitFlags.Bit2
        CheckBoxList1.Items(0).Selected = b And BitFlags.Bit3
    Else
        Label5.Text = "Invalid value"
        Dim i As Integer
        For i = 0 To 3
            CheckBoxList1.Items(i).Selected = False
        Next
    End If
End Sub

Private Sub CheckBoxList1_SelectedIndexChanged(ByVal sender As _
        System.Object, ByVal e As System.EventArgs) _
        Handles CheckBoxList1.SelectedIndexChanged
    Dim i As Integer
    Dim bits As Integer = 0
    For i = 0 To 3
        bits = bits * 2 ' right shift one bit
        bits = bits - CheckBoxList1.Items(i).Selected ' converts to -1/0
    Next
    TextBox3.Text = bits
    Dim b As BitFlags = CType(bits, BitFlags)
    Label5.Text = "'" + b.ToString() + "'"
End Sub

Einige Hinweise: Die Konvertierung zu und von ShortDays wird in den beiden TextChanged-Handlern für die beiden TextBoxes behandelt. Wir haben dem Formular eine Schaltfläche hinzugefügt, aber es gibt keinen Handler dafür. Wenn Sie darauf klicken, wird das Formular lediglich übermittelt, und es wird nichts anderes ausgeführt.

Die verschiedenen Geänderten Ereignisse werden bei JEDER Formularübermittlung ausgelöst, wenn sich der Text/die Kontrollkästchen geändert haben, sodass Sie auf eine beliebige Schaltfläche klicken können, um diese Ereignisse auszulösen. Oder tun Sie hier, wie wir es getan haben: Legen Sie einfach die AutoPostBack-Eigenschaft der Textfelder und der Kontrollkästchenliste fest, und nachdem Sie das Textfeld geändert haben, wird das Formular automatisch veröffentlicht, sobald Sie die Tabulatortaste entfernt haben, oder das Kontrollkästchen, sobald Sie darauf klicken. Beachten Sie jedoch, dass dies fehlschlägt, wenn im Browser des Benutzers die Skripterstellung nicht aktiviert ist. Deshalb haben wir die Schaltfläche unten.

Die Konvertierung von einer Zeichenfolge in eine Enumeration (in TextBox1_TextChanged) ist etwas schwierig. Die Konvertierung erfolgt durch Aufrufen der shared/static Parse-Methode in System.Enum. Diese Methode löst jedoch eine Ausnahme aus, wenn die Analyse fehlschlägt. Daher müssen wir diese Ausnahme abfangen und behandeln. Beachten Sie, dass bei dieser Version von Parse die Groß-/Kleinschreibung nicht beachtet wird, wenn der letzte Parameter True ist. Beachten Sie, dass wir die .NET Val-Funktion von Visual Basic verwenden, um aus ganzzahligen Zahlen in eine Zeichenfolge zu konvertieren. Val ist praktisch, da es im Gegensatz zu CInt keine Ausnahme auslöst, wenn die Konverstion fehlschlägt, es gibt nur null zurück.

Die Konvertierung auf die andere Weise ist aufgrund der System.Enum.IsDefined-Methode einfacher, die uns mitteilt, ob der Enumerationswert definiert ist oder nicht. Dies funktioniert jedoch nicht für Kombinationen von Bitflags, sodass wir bei der Konvertierung in eine BitFlags-Enumeration in TextBox3_TextChanged auf einen Bereich getestet haben. Beachten Sie auch, dass wir den Operator And verwenden, um zu überprüfen, ob ein bestimmtes Bit in der Enumeration festgelegt ist. Dies ist nur möglich, wenn die Enumeration wie hier über das Flags-Attribut verfügt.

C#-Sondermember (Indexer und überladene Operatoren – NEU!!)

Obwohl sie vom .NET Framework nicht direkt unterstützt werden, unterstützt die C#-Sprache zwei weitere Typen von Membern: Indexer und überladene Operatoren. Einige .NET-Sprachen unterstützen diese direkt, andere nicht. Mit Visual Basic .NET können Sie (etwas umständlich) C#-Code verwenden, der Indexer und überladene Operatoren enthält, aber keine eigenen schreiben.

Mit Indexern können Sie Ihr Objekt tiefschreiben– sie sind wie ein überladener Array-Tiefgestelltoperator in C++. Sie werden zu einer Eigenschaft namens Item im Code, der ausgegeben wird, zusammen mit speziellen Attributen in den Metadaten. Dadurch können sie von Sprachen verwendet werden, die Indexer nicht direkt unterstützen. Darüber werden wir in einer zukünftigen Spalte sprechen.

C# unterstützt auch C++-ähnliche überladene Operatoren. Diese werden zu Methoden mit speziellen Namen, z . B. op_Addition, im Code, der ausgegeben wird , zusammen mit einigen speziellen Attributen.

Denken Sie daran, dass Sie immer ILDASM verwenden können, um zu sehen, was der Compiler für Sie generiert hat. Laden Sie einfach die .exe, und drücken Sie STRG+M.

Beachten Sie, dass Sprachen, die diese Features nicht unterstützen, diese im Allgemeinen weiterhin verwenden können, da der generierte Code Standard ist. Durch das gleiche Token könnten andere Sprachen andere nicht standardmäßige Konstrukte aufweisen.

Angeben der Barrierefreiheit von Membern

Wir haben bereits gesehen, dass Sie angeben können, dass Member privat/privat sind und nur für die Klasse zugänglich sind, in der sie deklariert sind. oder öffentlich/öffentlich, für jeden zugänglich. Es gibt auch mehrere andere Zugriffsebenen, die Sie angeben können.

Bevor wir fortfahren, müssen Sie lernen (oder sich aus dem letzten Mal erinnern), was eine Assembly ist. Eine Assembly ist die kleinste Einheit, die versioniert und im .NET Framework installiert werden kann. Sie besteht aus einer oder mehreren Dateien. In unseren einfachen Beispielen bestehen unsere Assemblys aus genau einer .exe Datei. Jede Assembly verfügt über ein Manifest in einer der Dateien: Sie können dies mithilfe von ILDASM lesen.

Wenn Sie die Schlüsselwort (keyword) protected/Protected verwenden, ist der Zugriff auf die aktuelle Klasse (z. B. privat) sowie auf alle Klassen beschränkt, die von Ihrer Klasse erben, unabhängig davon, in welcher Assembly sie sich befinden. Die .NET Framework bezeichnet diese Art der Barrierefreiheit als "Familie".

Wenn Sie die C#-Schlüsselwort (keyword) intern oder visual Basic .NET Schlüsselwort (keyword) Friend verwenden, wird allen Klassen in der Assembly, in der Sie sich befinden, Zugriff gewährt, unabhängig von der Vererbung. Die .NET Framework nennt diese Art von Barrierefreiheit "Assembly". (Hinweis: Die Visual Basic .NET-Verwendung des Schlüsselwort (keyword) Friend ähnelt nicht dem C++-Schlüsselwort (keyword) Friend.)

In C# können Sie die geschützte interne Barrierefreiheit (Protected Friend in Visual Basic .NET) angeben, d. h. geschützt (Familie) oder intern (Assembly) – mit anderen Worten, jede Klasse in der Assembly oder von dieser Klasse geerbt (unabhängig von der Assembly) kann auf den Member zugreifen. Die .NET Framework nennt dies "Familie oder Assembly".

Die .NET Framework unterstützt auch die Barrierefreiheit von "Family and Assembly" (wobei die zugreifende Klasse eine abgeleitete Klasse in derselben Assembly sein muss), aber es gibt keine Möglichkeit in C# oder Visual Basic .NET, dies auszudrücken.

Übrigens können Sie die -Klasse auch öffentlich/öffentlich machen, was bedeutet, dass auf sie von jeder Assembly aus zugegriffen werden kann, nicht nur von der Assembly, in der sie definiert ist. (Der Standardzugriff für Klassen ist intern/Friend.)

Werttypen

Die meisten Klassen, über die wir bisher gesprochen haben, sind Verweistypen, d. h. sie können nur auf dem Garbage Collection-Heap erstellt werden, indem der New/New-Operator verwendet wird, und dass die Variable selbst ein Verweis auf das Objekt ist, nicht auf das tatsächliche Objekt. Wenn Sie einen Verweistyp zuweisen, kopieren Sie den Verweis, nicht das Objekt, auf das er verweist, sodass mehrere Verweise auf dasselbe Objekt verweisen können. Für instance ist StringBuilder ein Verweistyp:

   using System.Text;
   // ....
   StringBuilder a, b; 
   // used StringBuilder because String is immutable
   a = new StringBuilder("Foo");
   b = a;          // a and b refer to same String
   b.Append('d');   // slightly more efficient to use char, not "d"
   Console.WriteLine("a: {0}, b: {1}", a, b); // both refer to "Food"

... oder in Visual Basic .NET:

   Imports System.Text
   ' ....
   Dim a, b as StringBuilder 
   ' used StringBuilder because String is immutable
   a = New StringBuilder("Foo")
   b = a          ' a and b refer to same String
   b.Append("d"C)    ' slightly more efficient to use char, not string
   Console.WriteLine("a: {0}, b: {1}", a, b) ' both refer to "Food"

Sowohl a als auch b verweisen auf dasselbe Objekt, sodass sich auch ein ändert, wenn wir b ändern. Dies ist im folgenden Diagramm zu sehen:

Die Semantik der integrierten Typen, z. B. int/Integer, unterscheidet sich. Sie werden im Stapel erstellt, sodass sie automatisch verschwinden, wenn die von Ihnen ausgeführte Methode endet. Daher ist weder eine dynamische Speicherzuordnung noch eine Garbage Collection erforderlich. Und wenn Sie sie zuweisen, kopieren Sie den Wert – es gibt keinen Verweis darauf. Alle integrierten Typen sind Werttypen, mit Ausnahme von Objekt/Objekt, Zeichenfolge/Zeichenfolge und den Arraytypen.

Mit der .NET Framework können Sie auch eigene Werttypen erstellen. In C# nennen Sie sie als Struktur (Struktur in Visual Basic .NET) und nicht als Klasse/Klasse. Werttypen werden implizit von System.ValueType abgeleitet (mit Ausnahme von Enumerationen, die implizit von System.Enum abgeleitet werden) und können nicht von einem anderen Typ abgeleitet werden. Es kann auch kein anderer Typ von ihnen abgeleitet werden– Werttypen sind versiegelt.

Werttypen haben nur eine begrenzte Verwendung, aber sie können sehr hilfreich sein, um Ihre Programme effizienter zu gestalten, indem sie die Speicherbelegungen reduzieren. Sie sollten einen Werttyp deklarieren, wenn der Typ wie ein primitiver Typ fungiert, Sie eine Wertsemantik haben möchten und die Daten klein sind (z. B. unter 16 Bytes). Große Werttypen sind eine schlechte Idee, da sie standardmäßig als Wert an Methoden übergeben werden. Wenn sie groß sind, kann das Kopieren für jeden Methodenaufruf viel Zeit in Anspruch nehmen.

Trotz ihrer Einschränkungen sind Werttypen äußerst praktisch für das Schreiben von Typen wie Punkten, Größen und komplexen Zahlen. Für instance kann ein komplexer Typ in etwa wie folgt in C# definiert werden:

   public struct Complex {    // struct, not class, for value types
      double real, imaginary; // private!
      public double Real {
         get {
            return real;
         }
         set {
            real = value;
         }
      }
      // ...and so on...
   }

... und in etwa wie folgt in Visual Basic .NET:

Public Structure Complex ' Structure, not Class, for value types
    Dim realValue, imaginaryValue As Double ' private!
    Public Property Real() As Double
        Get
            Return Real
        End Get
        Set(ByVal Value As Double)
            Real = Value
        End Set
    End Property
    ' ...and so on...
End Structure

Sie erstellen Werttypen auf die gleiche Weise wie bei jedem anderen Typ: mit dem Operator new/New . (Sie können sie auch ohne Initialisierung deklarieren, wie in "Complex a;" oder "Dim a as Complex", in diesem Fall werden alle Felder auf null initialisiert.) Der Standard Unterschied besteht darin, dass der Werttyp im Stapel und nicht im Garbage Collection-Heap zugeordnet wird.

Boxing und Unboxing

Eine der besten Features der .NET-Runtime ist, dass jeder Typ, einschließlich Werttypen, in ein Objekt/Objekt konvertiert werden kann. Verweistypen werden sowieso von Object abgeleitet, sodass die Konvertierung die normale implizite Konvertierung in den Basistyp eines Objekts ist.

Die Konvertierung eines Werttyps erfordert jedoch einige Arbeit. Was passiert, ist, dass der Compiler den Werttyp in einen gleichwertigen Verweistyp einboxt , indem er den geschachtelten Typ auf dem Heap erstellt und den Wert in ihn kopiert. Der geschachtelte Typ wird von System.ValueType abgeleitet.

Dieser Verweistyp kann dann in Object konvertiert werden und an jedem Beliebigen Ort verwendet werden , an dem ein Objekt verwendet werden kann – in Datensammlungen als Parameter für Console.WriteLine – überall! Aus diesem Grund können Sie in den meisten .NET-Sprachen Code wie "" oder "object o = 5;Dim o as Object = 5" schreiben. Das Ergebnis ist, dass o auf ein boxed int/Integer-Objekt verweist, das 5 enthält.

Im Folgenden sehen Sie ein Diagramm eines geschachtelten Objekts:

Wenn wir also Werttypen wie in "Console.WriteLine("a: {0}", a)" an Console.WriteLine übergeben, geschieht tatsächlich, dass ein in ein Objekt konvertiert wird, indem es boxt, und der Verweis auf das geschachtelte Objekt wird an Console.WriteLine übergeben, die die Format- oder ToString-Methode dafür aufruft.

Die Runtime unterstützt auch unboxing: Konvertieren des Typs des geschachtelten Werts wieder in einen Unboxed-Werttyp. Hierfür ist eine Umwandlung erforderlich, z. B. in "int i = (int)o;" oder "Dim i as Integer = CType(o, Integer)". Beim Aufheben der Boxing in Sprachen wie C# werden die Daten aus dem geschachtelten Objekt in den Arbeitsspeicher für den Werttyp kopiert. (In verwaltetem C++ ist es nicht erforderlich, die Daten zu kopieren.)

Beachten Sie, dass beim Boxen eine separate Kopie der Werttypdaten erstellt wird. Dies könnte überraschend sein, zumal boxen automatisch erfolgt. Außerdem möchten Sie Boxing- und Unboxing-Vorgänge nach Möglichkeit vermeiden. Dies sind zusätzliche Arbeit, die Ihre Programme verlangsamen wird.

Wenn Sie sich jemals fragen, was Ihre Programme tun, untersuchen Sie einfach den IL-Code mit ILDASM. Sie werden feststellen, dass es BOX - und UNBOX-Anweisungen für diese Vorgänge gibt. Während die BOX-Anweisung immer ein neues Objekt auf dem Heap erstellt, gibt die UNBOX-Anweisung lediglich die Adresse des unboxierten Teils des geschachtelten Objekts zurück. Verwaltetes C++ kann diese Adresse direkt verwenden. C# kann nicht und kopiert daher immer den unboxed-Teil in einen separaten Speicherbereich.

Spezielle Typen

Integrierte Typen

Es gibt eine Gruppe von Typen, die speziell sind, da sie in die .NET Framework integriert sind. Die meisten davon sind Werttypen. Die meisten Sprachen haben unterschiedliche Namen für diese Typen. Für diese Diskussion verwenden wir die Namen der Strukturen im Systemnamespace . Bool /Boolean (unten) ist also wirklich System.Boolean usw.

Am einfachsten ist bool/boolean. Sie kann die Werte true/True oder false/False darstellen. In C# müssen die Bedingungen in if-Anweisungen und -Schleifen vom Typ Boolean sein, wodurch der berühmte Fehler verhindert wird:

   int a; 
   if (a = 5) // ASSIGNMENT, not comparison; error in C# due to type

da der Typ eines nicht bool ist. (Visual Basic .NET hat dieses spezielle Problem noch nie aufgetreten.)

Es gibt auch den Zeichentyp char/Char, der ein einzelnes 16-Bit-Unicode-Zeichen enthält, und den Zeichenfolgentyp string/String, der eine Zeichenfolge von Unicode-Zeichen enthält. Der Zeichenfolgentyp ist ein Verweistyp.

Es gibt eine Reihe von ganzzahligen Typen, mit Vorzeichen und ohne Vorzeichen, die von 8 Bit bis 64 Bit reichen. Die Typen mit Vorzeichen sind sbyte/SByte, short/Short/Int16, int/Integer/Int32 und long/Long/Int64. Beachten Sie, dass Visual Basic .NET keinen signierten Bytetyp (SByte) aufweist und dass signiertes Byte kein CLS-kompatibler Typ (Common Language Specification) ist.

Die .NET Framework verfügt außerdem über einen ganzzahligen Typ der systemeigenen Wortgröße des Computers namens System.IntPtr.

Die ganzzahligen Typen ohne Vorzeichen sind byte/Byte, ushort/UInt16, uint/UInt32, ulong/UInt64 und UIntPtr. Davon unterstützt Visual Basic .NET nur Byte. Keine dieser Typen außer Byte sind CLS-kompatible Typen, sodass sie nicht für die sprachübergreifende Programmierung verwendet werden können.

Es gibt zwei Gleitkommatypen, float/Single und double/Double, die 32- und 64-Bit-Binäre Gleitkommazahlen darstellen. Und dezimal/dezimal ist ein Typ, der dezimale Brüche genau (ohne Rundungsfehler) darstellt, sodass Zahlen wie 0,1 und 0,01, die nicht genau im binären Gleitkomma dargestellt werden können (genau wie 1/3 nicht genau im Dezimalwert dargestellt werden kann– es ist das sich wiederholende Dezimalzeichen 0,33333...), genau dargestellt werden können. Dadurch erhalten Sie eine genauere Darstellung und mathematische Darstellung, wenn Sie mit Währung arbeiten.

Und natürlich ist object/Object ein integrierter Verweistyp.

Sprachunterstützung für integrierte Typen

Einige dieser Typen verfügen über integrierte Unterstützung in verschiedenen Sprachen. Für instance enthält die C#-Sprache Literale für bool (true und false), Zeichen, Zeichenfolgen, die Ganzzahltypen (in Dezimal- oder Hexadezimalzahl, aber nicht oktal; suffixiert mit u oder U für unsigniert und/oder l oder L für 64-Bit) und die Gleitkommatypen (suffixiert mit f oder F für float/Single oder m oder M für dezimal/dezimal). Visual Basic .NET enthält auch Literale für alle unterstützten integrierten Typen, z. B. das Anfügen von "C" an einstellige Zeichenfolgen, um sie zu Char, "D" zu Zahlen zu machen, um sie zu Decimal, "R" zu Zahlen zu machen, um sie zu Double zu machen, oder "I", um eine Zahl integer zu machen.

Delegaten

Ein Delegat ist ein Objekt, das einen Verweis auf eine Methode und, wenn die Methode eine instance Methode ist, auf ein bestimmtes Objekt enthält. Sie können einen Delegaten auf jede geeignete Methode verweisen lassen, und Sie können die Methode jederzeit über den Delegaten aufrufen. Sie sehen also, dass Delegaten die gleiche Funktion wie ein Funktionszeiger in C oder C++ haben. sie sind jedoch typsicher und sicher. Sie werden häufig für Rückrufe und für Ereignisse verwendet.

Um einen Delegaten zu deklarieren, beschreiben Sie die Parameter und geben den Typ der Methode zurück, die der Delegat aufrufen kann, wie im folgenden C#-Code:

public delegate void MyDelegateType(int a);

In Visual Basic .NET sieht die obige Delegatdeklaration wie folgt aus:

Public Delegate Sub MyDelegateType(ByVal a As Integer)

Dadurch wird MyDelegateType als Delegat deklariert, der eine Methode aufrufen kann, die eine int/Integer-Methode akzeptiert und nichts zurückgibt. Dies entspricht der Ausführung einer Typdefinition eines Funktionszeigertyps in C oder C++.

Anschließend deklarieren Sie eine Delegatvariable– dies entspricht dem Deklarieren und Initialisieren des tatsächlichen Funktionszeigers in C oder C++. Im Folgenden finden Sie den C#-Code für diesen Code:

   // for an instance method
MyType o = new MyType();
   MyDelegateType MyDelegate = new MyDelegateType(o.MyMethod);
   // ...or, for a static method:
   MyDelegateType MyDelegate = 
new MyDelegateType(MyType.MyStaticMethod);

Und hier ist der VB .NET-Code:

' for an instance method:
Dim o As New MyType()
Dim MyDelegate As New MyDelegateType(AddressOf o.MyMethod)
' ...or, for a Shared method:
Dim MyDelegate2 As _
New MyDelegateType(AddressOf MyType.SharedMethod)

Dadurch wird eine Variable MyDelegate deklariert, die auf MyObject.MyMethod(int) verweist und nichts zurückgibt. Wenn keine Überladung von MyMethod vorhanden ist, die mit dem Rückgabetyp und der Parameterliste des Typs MyDelegate übereinstimmt, erhalten Sie einen Fehler.

Außerdem deklariert er einen zweiten Delegaten, MyDelegate2, der sich auf eine statische/Shared-Methode bezieht, die eine int/Integer-Methode akzeptiert und nichts zurückgibt.

Wenn Sie einen Aufruf mithilfe des Delegaten durchführen, ist es so, als ob Sie die -Methode für das angegebene Objekt aufrufen:

   MyDelegate(5)

Es ist wirklich so einfach. Und die Macht besteht darin, dass MyDelegate auf jede Methode in jedem Objekt (oder einer statischen Methode) verweisen (und aufrufen kann), die eine int/Integer-Methode akzeptiert und nichts zurückgibt.

Namespaces

Wir haben Namespaces bereits erwähnt, hauptsächlich im Kontext der using/Imports-Anweisung . Ein Satz von using-/Imports-Anweisungen sagt dem Compiler: "Wenn Sie keinen Bezeichner im aktuellen Namespace finden können, durchsuchen Sie jeden der namespaces, die in diesen using/Imports-Anweisungen angegeben sind."

Daher haben wir für instance die Konsolenklasse umfassend genutzt. Die using System;/Imports System-Anweisung weist den Compiler an, zuerst die Konsole allein und dann System vorab zu testen. Nur wenn die Konsole in keinem der Namespaces gefunden wird, meldet der Compiler einen nicht deklarierten Bezeichner.

Daher sind Namespaces nur eine Kurzform für die Benennung von Klassen, und using/Imports ist eine Abkürzung für den Verweis auf Klassen. nicht mehr, nicht weniger.

Sie können Ihre eigenen Namespaces deklarieren. Schließen Sie Ihr Programm einfach wie in diesem C#-Beispiel in einen Namespace-/Namespaceblock ein:

   namespace DrGUI.FirstNamespace {
      // class and other declarations go here
      class Foo {  // actually DrGUI.FirstNameSpace.Foo
         // ...
      }
   }

... oder wie in diesem Visual Basic .NET-Beispiel:

Namespace DrGUI.FirstNamespace
    ' class and other declarations go here
    Class Foo ' actually DrGUI.FirstNameSpace.Foo
        ' ...
    End Class
End Namespace

Diese Namespaces können geschachtelt werden. Wenn Sie einen Namespace verwenden, ändern Sie die Namen aller Typen, die Sie innerhalb dieses Namespace deklarieren.

Um Namespacekonflikte zu vermeiden, empfiehlt die .NET Framework, Dass Sie Ihre Namespaces mit Ihrem Unternehmensnamen und einem Technologie- oder Projektnamen wie Microsoft.Win32 benennen.

Wir überspringen die Verwendung von Namespaces für die einfachen Beispiele, die wir hier ausführen. Aber für größere Programme sind sie in der Tat sehr praktisch.

Namespaces sind nicht identisch mit Assemblys.

Der Name einer bestimmten Sache und die Assembly, in der es lebt, sind zwei verschiedene Dinge. Bei Namespaces geht es darum, lange Namen verschiedener Typen zu erstellen und zu verwenden. Die Assembly, in der sie sich befindet, ist die .exe oder DLL, in der sie sich befindet. Dies sind zwei völlig getrennte Dinge.

Sie können also über mehrere Namespaces in einer bestimmten Assembly verfügen, und die Typen in einem bestimmten Namespace können auf mehrere Assemblys verteilt werden. Namespaces und Assemblys sind überhaupt nicht miteinander verknüpft, sie sind vollständig orthogonal.

Versuch es doch mal!

Wenn Sie .NET haben, ist es die Möglichkeit, es zu lernen, es auszuprobieren ... und wenn nicht, erwägen Sie es. Wenn Sie eine Stunde oder so eine Woche mit Dr. GUI .NET verbringen, sind Sie ein Experte im .NET Framework, bevor Sie es wissen.

Seien Sie der Erste in Ihrem Block – und laden Sie einige Freunde ein!

Es ist immer gut, der Erste zu sein, der eine neue Technologie lernt, aber noch mehr Spaß, dies mit einigen Freunden zu tun! Organisieren Sie für mehr Spaß eine Gruppe von Freunden, um gemeinsam .NET zu lernen! Wenn Sie keine Freunde haben, machen Sie etwas – vielleicht durch einen Besuch des Nachrichtenboards. (Aber denken Sie daran, dass die Meldungstafel auch für Personen gedacht ist, die FREUNDE haben.)

Einige Dinge, die Sie ausprobieren können...

Testen Sie zunächst den hier gezeigten Code. Ein Teil davon stammt aus größeren Programmen; Sie müssen das Programm um diese Codeausschnitte herum aufbauen. Das ist eine bewährte Methode.

Testen Sie als Nächstes einige der neuen Features der .NET Framework. Testen Sie Eigenschaften – sie sind sehr cool. Versuchen Sie, schreibgeschützte und schreibgeschützte Eigenschaften zu erstellen. Sie können sogar Delegaten wie Funktionszeiger verwenden – sie sind ziemlich einfach, flexibel und cool. Probieren Sie einige Enumerationen, einschließlich Flags, und vielleicht sogar eine oder zwei geschachtelte Klassen aus.

Probieren Sie einen Werttyp aus, lesen Sie jedoch unbedingt die Dokumentation zu den Einschränkungen für Werttypen, bevor Sie sich gegen diese wenden. Verwenden Sie für weitere zusätzliche Guthaben überladene Operatoren in C# in Ihrem Werttyp.

Deklarieren Sie als Nächstes ein oder zwei Klassen in einem Namespace, und überprüfen Sie die Metadaten, um zu sehen, was sich geändert hat. Erstellen Sie die richtigen using/Imports-Anweisungen , um über andere Namespaces auf Ihre Klassen zuzugreifen.

Wenn Sie sich in die Dokumentation einlesen möchten, um herauszufinden, wie Sie Programme erstellen, die mehrere Module und Assemblys umfassen, versuchen Sie, ein Multi-Assembly-Programm zu erstellen und die verschiedenen Schutzmodifizierer auszuprobieren.

Was wir getan haben; Was kommt als nächstes

Dieses Mal haben wir über die Grundlagen des .NET Framework Typsystems gesprochen und ein wenig gezeigt, wie die .NET Framework sie implementiert.

Beim nächsten Mal werden Vererbung, Schnittstellen, Polymorphie und Überschreibungsmethoden erläutert.