Inhaltsverzeichnis         

3 .NET

3.1 Versionen

Version

Erscheinungsjahr

Anmerkung

0.?

2000

Vorstellung des .NET Konzeptes auf der Entwicklerkonferenz PDC

1.0

2002

Auslieferung der Version 1.0 mit zugehörigen SDK

1.1

2003

Geringfügig verbessertes SDK 1.0 + Visual Studio 2003

2.0

2005

Vollständig überarbeitetes Framework +m Visual Studio 2005

  • generische Klassen

  • partielle Klassen

  • In großen Teilen neu impelmentiertes ASP.NET

  • stark verbessertes Intellisense in VS 2005

  • viele neue Designer in VS2005

3.0

2006

Erweiterungen um Klassenbibliotheken speziell für Windows Vista:

  • Windows Communication Foundation (WCF)

  • Windows Presentation Foundation (WPF)

  • Windows Workflow Foundation (WF)

  • Windows CardSpace (WCS)

3.5

2008

Baut auf .NET 2.0 auf und fügt im wesentlichen hinzu:

  • Language Integrated Query (LINQ)

  • ASP.NET mit AJAX

Visual Studio 2008, kann jetzt auch Projekte für .NET 2.0 und .NET 3.5 verwalten

4.0

2010

Neue CLR und neuer Global Assembly Caches (GAC) nun C:\Windows\Microsoft.NET\assembly.

  • Client Profile: abgespeckte Version der CLR, die auf einer größeren Anzahl von Plattformen bereitgestellt werden kann

  • Paralleler Betrieb von CLR's verschiedener Versionen im gleichen Prozess

  • GC läuft jetzt im Hintergrund

  • Codeverträge (System.Diagnostics.Contracts): Definition von Vor-, Nechbedingungen und Objektinvarianzen von Methodenaufrufen möglich

  • Erweiterung der CLR um die DLR zur Implementierung dynamischer Sprachen

  • Klassenbibliotheken für BigInteger, komplexe Zahlen und Tupel

  • verzögerte Instanziierung mittels System.Lazy<T>

  • Integration des Observer- Designpatterns mit System.IObservable<T>

  • Tasks- neues Programmiermodell für parallel- Computing

4.5

2012

Neue CLR. Jedoch MSIL kompatibel mit 4.0-> 4.5 exe läuft auch unter 4.0, falls keine neuen Klassen eingesetzt werden.

Baut auf 4.0 auf (inplace- Installation).

Neuerungen:

  • Apps für Windows AppStore entwicklen

  • Portable Klassenbibliotheken zur Plattformübergreifenden Programmierung auf Windows Phone und PC

  • Arrays > 2GB auf 64bit Plattformen werden unterstützt

  • JITCompiler und GC laufen jetzt im Hintergrund (asynchron)

  • UniCode- IO auf der Kommandozeile (Console.WriteLine) jetzt möglich !

  • ASP.NET 4.5

    • HTML 5 Formulare

    • Modellbindung in WebForms (analog MVC)

    • WebSockets

Windows Server 2003 und Windows XP werden nicht mehr unterstützt !



3.2 Onlinehilfe

Zum SDK wird eine umfangreiche Onlinehilfe mitgeliefert. Sie ist die primäre Dokumentation zum .NET Framework.

3.2.1 Zum angezeigten Thema den zugehörigen Eintrag im Inhaltsverzeichnis finden

Zu jeder Hilfetafel kann der zugehörige Eintrag im Inhaltsverzeichnis aufgeklappt werden über die Funktion Mit Inhaltsverzeichnis synchronisieren welche über den Symbolleistenlink gestartet wird. So können über Index oder Suchen gefundene Tafeln schnell in den gesamten Kontext des Dokumentes eingeordnet werden.

3.3 Das .NET

Das .NET ist im Wesentlichen eine Spezifikation für ein Framework zur Programmierung in einer vernetzten Welt. Durch Implementierungen von .NET werden Programme plattformneutral (unabhängig von der CPU und dem Betriebssystem), resourcenschonend und kontrollierbar während der Ausführung (Sicherheit). Die Spezifikation gliedert sich in folgende Teile:



CLR

Common Language Runtime: Definiert eine eine einheitliche Laufzeitumgebung für .NET Programme. Diese besteht aus:

  • Einem Just In Time Compiler zur Übersetzung der MSIL (Microsoft Intermediate Language) in die sog. systemeigene Maschinensprache (z.B. x86 Maschinensprache)

  • Einer automatischen Heapspeicherverwaltung, genannt GC (Garbage Collector)

  • Einer Prüfung beim Ladevorgang, der die Typsicherheit des Codes prüft

  • Einem System zur Gewährleistung der Zugriffssicherheit zur Laufzeit (Code Access Security)

  • Konfiguration einer Anwendung mittels XML- Dateien

CLS

Common Language Specification: Definiert ein Subset von Merkmalen, die alle Programmiersprachen im .NET erfüllen müssen. Sprachen wie C# oder C++ besitzen über die CLS hinausgehende Sprachfeatures wie unit (C#) oder Referenztypen auf dem Stack (C++). Um Interoperabilität zwischen den Sprachen zu gewährleisten, sollten CLS- konforme Passagen im Programm mittels des Attributes CLSCompliantAttribute markiert werden. Der C# Compiler überprüft die markierten Passagen und gibt im Falle von nicht konformen Code zur Übersetzungszeit einen Fehler aus (C++ hat diese Komfortfunktion nicht!)

CTS

Common Type Specification: Definiert elementare Datentypen auf der Bitebene als Grundlage der eingebauten Datentypen für alle .NET Programmiersprachen. Festkommatypen werden dabei nach little Endian (niederwertige Bytes auf den kleineren Adressen, höherwertige Byte auf den größeren Adressen) dargestellt, und Zeichen oder Zeichenketten durch UTF16 kodiert.

3.3.1 Schichtenmodell




3.4 Windows RT, Apps und Appstore (ab 4.5)

Mit Windows 8 wurde eine neue API in der Windows- Betriebssystemfamilie eingeführt: die Windows Runtime oder kurz WinRT. WinRT ist ein objektorientierte Betriebssystem- API. Bei ihrer Entwicklung diente .NET als Blaupause. Viele Klassen aus WinRT ähneln .NET Klassen. Jedoch ist die WinRT eine in C++ geschriebene COM- Komponente. Sie ist damit kein Ersatz für das .NET Framework !






3.4.1 Vorteile von WinRT

  1. Geringer Ressourcenverbrauch und hohe Ausführungsgeschwindigkeit dank optimierter Übersetzung durch C++ Compiler

  2. Portable API für spezielle Windowsanwendungen, genannt Apps (z.B. lauffähig auf Desktop und Windows Phone)

  3. Innovative grafische Benutzeroberfläche mit Gestensteuerung im "Kacheldesign" (Tiles).

  4. Programmierbar in C++, HTML/Javascript und mittels spezieller CLR auch in C#/VB.NET und XAML

3.4.2 Nachteile von WinRT

  1. Läuft erst ab Windows 8- Windows 7, Vista, XP werden nicht unterstützt !

  2. Kein Garbage Collector. Stattdessen Speicherverwaltung durch Referenzzählung wie in COM üblich.

  3. Apps haben aus Sicherheitsgründen ähnlich wie Webanwendungen im Browser einen auf Minimum beschränkten Zugriff auf lokale Ressourcen (z.B. Dateisystem, Netzwerk). Echte Desktop Anwendungen sind nicht ohne tiefgreifende Änderungen in der Architektur in Apps umprogrammierbar.

  4. Apps können nur von einem speziellen Server, dem Appstore geladen werden.

  5. Browser basierte Webanwendungen und Apps haben das gleiche Einsatzgebiet (plattformneutraler Zugriff auf Inhalte im Netz). Jedoch sind die Browser basierten Anwendungen wesentlich portabler. So ist eine Browser basierte Anwendung auf jedem Gerät ausführbar, das einen Browser anbietet – das sind fast alle (PC's, Mobilgeräte). Apps sind nur auf Windows 8 und Windows Phone ausführbar. Auch stehen die Browser basierten Anwendungen in ihrer funktionalen Möglichkeiten dank HTML5 Erweiterungen wie Canvas und local Storage den Apps in keiner Weise nach.

3.4.3 Fazit

Wer dem Zeitgeist dienen muss innerhalb der Windows- Plattform, kommt an Apps und WinRT nicht vorbei. Ob der Zeitgeist auch in Zukunft den Ton angeben wird, kann ruhig bezweifelt werden. Denn WinRT Apps funktionieren nur auf Windows 8/Windows Phone. Die Masse nutzt derzeit jedoch IPhones mit Mac IOS oder Android Geräte. Jede dieser alternativen Plattformen hat sein eigenes, mit den anderen völlig inkompatibles Appframwork. Wer also alle erreichen möchte, muss für jede Plattform die inhaltlich gleiche App schreiben. Mit einer Webanwendung erreicht man hingegen alle. Das ist nicht nur billiger, es sichert auch langfristige Projekte.

3.5 Applikationsdomänen

Das Windows- Betriebssystem teilt die Ressourcen wie Arbeitsspeicher, Dateizugriff, und Prozessorzeit über Prozesse auf Anwendungen auf. Jede laufende unverwaltete Anwendung wie z.B. Excel belegt dabei mindestens einen Prozess.

Ein Prozess stellt einen virtuellen Adressraum bereit, der den theoretisch größtmöglichen Arbeitsspeicher überdeckt. Die Zuordnung der virtuellen Adressen zu realen erfolgt durch das Betriebssystem. Dabei teilt es den Adressraum in gleichgroße Teile (Kacheln) auf. Diese können

  1. realen Arbeitsspeicherabschnitten zugeordnet,

  2. auf Festplatte ausgelagert (Swap - Datei)

  3. oder dem Prozess noch nicht zugeordnet sein.

Vorteile der Prozesse:

  1. Mehrere Anwendungen teilen sich den physischen Arbeitsspeicher (Kacheln verschiedener Prozesse werden Arbeitsspeicherbereichen zugeordnet)

  2. Anwendungen sind voneinander isoliert (Einschluss im eigenen virtuellen Adressraum)

  3. Fehlfunktionen einer Anwendung sind auf das Scheitern des Prozesses beschränkt.

Nachteile von Prozessen:

  1. Wechsel zwischen Anwendungen bewirken Prozessumschaltungen – ressourcenintensiv

Die CLR ermöglicht eine zusätzliche Aufteilung eines Prozesses auf mehrere .NET Anwendungen durch Applikationsdomänen. Der Lader der CLR platziert die Assemblies der Anwendungen auf disjunkte Adressbereichen innerhalb eines Prozesses. Die Zugriffe auf den managed Heap werden geprüft. Der Versuch, auf die Instanzen einer anderen Applikationsdomäne auf dem Managed Heap zuzugreifen, wird von der CLR unterbunden. Hierdurch können die Nachteile von Prozessen überwunden werden. Das wirkt sich insbesondere auf Serversystemen aus, die mehrere Anwendungen (z.B. ASP.NET) für hunderte von Usern anbieten.




3.6 Assemblies

Übersetzter .NET Code wird in Dateien gespeichert, die als Assemblies bezeichnet werden. Eine Assembly besteht aus einem Manifest, Typmetadaten und dem MSIL- Code, der durch Übersetzen der C# - oder VB.NET Anweisungen entsteht.

Durch die aus den Typmetadaten abgeleitete Strukturierung des MSIL- Codes kann eine Assembly auch als objektorientierter Assemblercode betrachtet werden (http://stackoverflow.com/questions/216841/how-does-mono-work).

Im Manifest werden Name, Version und Kultur der Assembly definiert. Der Name kann um einen fälschungssicheren Strong- Name erweitert werden. Dieser entsteht durch Verschlüsseln des Manifestes einer Assembly mit einem privaten Schlüssel, der nur dem Entwickler bekannt ist.

3.6.1 Aufbau einer Assembly




3.6.2 Versionsnummer

Aus dem aktuellen Stand seines Programmcodes kann der Programmierer eine Assembly erstellen. Diese "Momentaufnahme" bekommt eine eindeutige Nummer in Raum und Zeit, die Versionsnummer. Die Versionsnummer einer Assembly besteht aus folgenden vier Teilen, wobei jeder Teil ein Wert aus dem Intervall [0, 65535] sein darf:


Hauptversion

Nebenversion

Build

Revision

Beispielwerte

1

0

12

1

Änderungen in der Assembly, wenn sich Wert erhöht (++)

Neue Assembly mit neuen oder erweiterten Funktionen, die inkompatibel mit den Vorgängern ist

Neue Assembly, die konzeptionell den Funktionsumfang der Hauptversion hat, gegenüber den Vorgängern aber besser implementiert. Inkompatibel mit den Vorgängern

Neu erstellte Assembly, die kompatibel mit allen Vorgängern ist, welche die gleiche Haupt- und Nebenversion tragen

Neu erstellt mit Fehlerkorrekturen

Bei Assemblies mit starkem Namen wird beim Binden an die Anwendung von der Laufzeit geprüft, ob die von der Anwendung geforderte Versionsnummer mit der Versionsnummer der Assembly übereinstimmt.

3.6.2.1 Dateiversion vs. Assemblyversion

Für eine Assembly können zwei Versionsnummer definiert werden:

Assemblyversion

An dieser orientiert sich der .NET Lader. Wenn ein .NET Programm eine Assembly in der Version 3.5.0.0 wünscht, dann wird genau nach einer solchen mit dieser Assemblyversion gesucht. Die Assmeblyversion sollte für ein Projekt deshalb fest definiert- und während des Projektes nicht geändert werden.

Dateiversion

Die Dateiversion wird beim Installieren auf dem Zielsystem berücksichtigt. Wenn während eines Projektes ständig neue Build's erfolgen, kann hier die Build- Nummer hochgezählt werden (z.B. 3.5.29.0), damit beim Installieren auf dem Testsystem tatsächlich eine neue DLL installiert wird.



3.6.3 Global Assembly Cache

Informationen zu allen Assemblys, die von mehreren Anwendungen gemeinsam benutzt werden können, werden im Global Assembly Cache (GAC) gespeichert. Dieser kann eingesehen werden über

// bis .NET 3.5
c:\Windows\assembly. 

// ab .NET 4.0
C:\Windows\Microsoft.NET\assembly

In diesen Verzeichnissen werden die DLL's in einer Ordnerstruktur mit folgendem Aufbau gespeichert:

assembly
  +-- GAC_32
  |     :
  |     +-- System.Data
  |     :    +- v4.0.0.0.0_b77a5c561934e089
  |             +- System.Data.dll
  |
  +-- GAC_MSIL
        :
        +-- System
        :    +- v4.0.0.0.0_b77a5c561934e089
                +- System.dll

Der Zugriffsrechte auf diese Ordner sind so eingestellt, das alle .NET Anwendungen auf dem System diese Assemblies laden können.

Die Installation im GAC kann mittels dem GACUtil.exe Kommandozeilentool, oder einem Setup- Projekt erfolgen.

3.6.3.1 Sicherung öffentlicher Assemblies mittels Strong Names

Durch Verschlüsselung von Daten aus dem Manifest einer Assembly (Name, Version, Kultur, Public Key und digitale Signatur) mittels eines geheimen privaten Schlüssels erzeugt der Entwickler einen sogenannten Strong Name. Die Anwender der Assembly bekommen vom Entwickler einen öffentlichen Schüssel ausgehändigt, der zum geheimen privaten Schlüssel passt. Mit dem öffentlichen Schlüssel können die Anwender die Manifestdaten aus dem Strong Name korrekt entschlüsseln. Das Verfahren ist so ausgelegt, das Personen, die nicht im Besitz des geheimen privaten Schlüssels sind, keine Strong Names bilden können, die sich korrekt mit den öffentlichen Schlüsseln entschlüsseln lassen.

Programme, die Funktionen einer Strong- Name Assembly aufrufen, müssen den öffentlichen Schlüssel zu einem Strong Name kennen. Dieser Schlüssel wird entweder im globalen Assemblycache beim Installieren der Strong- Name Assembly hinterlegt, oder in der Konfigurationsdatei mittels <assemblyIdentity...> und <codeBase...> Elementen definiert.

Strong Names haben folgende Eigenschaften:




3.6.3.2 Erstellen eines Schlüsselpaares zur Gewinnung von Strong- Names

Ein Schlüsselpaar kann mittels des Kommandozeilentools sn wie folgt gewonnen werden:

c:\sn.exe -k MyCompanyKeys.snk

Achtung: Die Datei mit dem Schlüsselpaar muß sicher aufbewahrt werden.

3.6.3.3 Signieren einer Assembly mit einem Strong Name

Eine Assembly wird vom Entwickler mit einen Strong Name signiert, indem auf die Schlüsseldatei mittels eines Attributes in der Datei AssemblyInfo.cs verwiesen wird:

[assembly:AssemblyKeyFileAttribute(@"MyCompanyKeys.snk")]

Der Verweis muss sich dabei relativ auf das Verzeichnis beziehen, indem die Assembly kompiliert wird. Da Visual Studion die Assemblys nicht im Projekt, sondern in den Verzeichnissen Debug\bin bzw. Release\bin kompiliert, muss der Bezug dann folgendermaßen formuliert werden (wenn die Schlüsseldatei sich im Projektverzeichnis befindet):

[assembly:AssemblyKeyFileAttribute(@"..\\..\\MyCompanyKeys.snk")]

3.6.3.4 Installation im Global Assembly Cache

Die Installation und Deinstallation kann mit einem Installationsprogramm oder dem Tool GACUtil.exe erfolgen.

3.6.3.5 Zugriff auf öffentliche Assemblies




3.6.3.6 Merksätze für Assemblies mit starken Namen

3.6.4 Festlegen der Speicherorte von privaten Assemblies, welche eine Anwendung einbindet

Private Assemblies werden gewöhnlich im Verzeichnis der Anwendung abgelegt. Hat die Verzeichnisstruktur der Anwendung einen komplexeren Aufbau, dann kann dies in der Anweundungskonfigurationsdatei beschrieben werden durch das probing Element.

<configuration>
   <runtime>
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="bin;bin2\subbin;bin3"/>
      </assemblyBinding>
   </runtime>
</configuration>

Befindet sich eine Anwendung im Verzeichnis C:\MyApp, dann werden durch die obige Konfiguration die Assemblies im Verzeichnis C:\MyApp\bin, C:\MyApp\bin2\subbin und C:\MyApp\bin3 gesucht.

3.7 Just in time Compiler

Auf folgendem strukturierten Zeitstrahl kann die Wirkungsweise des Just in Time Compilers abgelesen werden.

3.7.1 Optimierungen

3.7.1.1 Rate des Inlinings erhöhen (ab .NET 4.5)

Kleine Unterprogramme/Methoden, die häufig aufgerufen werden, können einen enormen Overhead durch die Parameterübergabe verursachen. Deshalb werden beim JIT- Compilieren Methoden, die z.B. nicht virtuell und kleiner als 32 Byte MSIL Code sind, nicht aufgerufen sondern an der Aufrufstelle direkt eingebettet.

Methoden, die die Längenbeschränkung überschreiten aber dennoch gut Kandidaten für Inlining sind, annotiert werden mit System.Runtime.CompilerServices.MethodImpl(MethodImplOptions.AggressiveInlining):



3.8 Werte-, Referenztypen und Garbage Collector

Objekte primitiver Typen wie Integer oder Double werden in .NET wie in jeder höheren, block- orientierten Programmiersprache im Stapelspeicher abgelegt. Man spricht von Wertetypen.

Objekte selbstdefinierter Klassen hingegen müssen manuell mittels New- Operator im Freispeicher platziert werden, wobei mit einer im Stapelspeicher befindlichen Referenz- Variable auf diese verwiesen wird. Man fasst diese auch unter dem Begriff Referenztypen zusammen.

In klassischen Programmiersprachen muss der Programmierer mit einem Befehl das Objekt wieder löschen, wenn es nicht mehr benötigt wird, um so den belegten Platz im Freispeicher freizugeben und damit wiederverwendbar zu machen. Häufig jedoch wurde diese Aufgabe unzureichend gelöst, was insbesondere bei langlaufenden Programmen (z.B. Serveranwendungen) zu einem "volllaufen" des Freispeichers mit nicht mehr genutzten Objekten führte. Letztendlich stürzte das Programm dann ab (Memory- Leak).


In .NET wird diesem Problem mit dem Garbage Collector begegnet. Dies ist ein Task der CLR, welcher in nicht vorher bestimmbaren Zeitraster startet, alle Objekte, die noch über Referenz- Variablen im Stapel adressiert werden als gültig markiert, um anschließend alle ungültigen Objekte zu löschen. Hierdurch werden automatisch alle nicht mehr benötigten Objekte zeitnah aus dem Freispeicher entfernt und seine Speicherkapazität zu jedem Zeitpunkt maximiert.






3.9 Object- "Mutter" aller Datentypen

Alle Typen sind auf den Basistyp System.Object rückführbar. D.h. alle Typen verfügen über die Eigenschaften und Methoden von System.Object.




3.9.1 Referenztypen vs. Wertetypen

Die Menge aller Typen, die sich von System.Object ableiten, kann in zwei Teilmengen aufgeteilt werden: den Referenztypen und den Wertetypen. Die Wertetypen werden grundsätzlich auf dem Programmstack angelegt und entsprechen den eingebauten Typen in älteren Programmiersprachen.


Referenztypen erfordern eine Instanziierung in 2 Schritten:

3.9.2 Boxing und Unboxing- Konvertierung

Zwecks Erhöhung der Ausführungsgeschwindigkeit werden Wertetypen auf dem Stack wie in allen anderen nicht .NET- Sprachen als reine Werte abgelegt. Da aber alle Typen komplexe Objekte darstellen, erfolgt eine Konvertierung, wenn ein Wertetyp in einem Objektkontext aufgerufen wird. Dabei wird temporär auf dem Heap ein Objekt angelegt, und der Wert des Wertetypen hineinkopiert. Dies wird als boxing  bezeichnet.

              // i ist ein Wertetyp
            int i = 99;

            // obj_1 ist ein Referenztyp und zeigt auf nichts
            object obj_1;

            // obj_2 ist ein Referenztyp und zeigt auf eine Variable auf dem Stack (?!)
            // -> Boxing
            object obj_2 = i;

            Console.WriteLine("Wert von obj_2= " + obj_2.ToString());
            Console.WriteLine("Wert von i= " + i.ToString());

            i++;

            // Die folgende Ausgaben zeigen, daß obj_2 auf einen anderen Speicherbereich 
            // zeigt als i. 
            Console.WriteLine("Wert von obj_2= " + obj_2.ToString());
            Console.WriteLine("Wert von i= " + i.ToString());

3.9.3 Spezialwert null

Nicht initialisierte Referenzen haben immer den Wert null .

3.10 Typsicherer Code

Im folgenden wird unter Code ein in die Maschinensprache übersetztes Programm verstanden.

Ein Code ist Typsicher, wenn er folgenden Kriterien erfüllt:

  1. Ein Verweis auf einen Typ ist vollständig kompatibel mit dem Typ, auf den verwiesen wird

  2. Für ein Objekt sind nur die zu seinem Typ passenden Operationen aufrufbar

  3. Identitäten sind, was sie von sich behaupten

Bespiel:

  
    using
     System;
  
using System.Collections.Generic;
using System.Text;

namespace u1_TypeSafety
{
    class Program
    {
        // Klasse für Punkte in der Ebene
        public class CPoint
        {
            public int x, y;
        }

        // Klasse für Linien in der Ebene
        public class CLine
        {
            public CPoint start, end;
        }

        static void Main(string[] args)
        {
            try
            {
                CPoint p1 = new CPoint();
                CLine l1 = new CLine();

                // Impliziter Cast eines Punktes in ein object
                object obj = p1;

                // Verbotener Cast, der durch den Compiler entdeckt wird
                // CLine l2 = (CLine)p1;

                // Verbotener Cast, der durch die CLR entdeckt wird
                CLine l2 = (CLine)obj;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Fehler: ", ex.Message);
            }

        }
    }
}