Home
Themen
Angebote
Downloads
News & Blogs
    ZfS News
    .NET & C#
    ASP.NET & Sharepoint
    ADO.NET & Datenbanken
    Windows
    Lehre
    Blogs nach Personen
Informatiklandkarte
DBWorld Map
.NET Glossar
Wir über uns
Kontakt
Newsletter

News

Monday, July 31, 2006

Was ist 'boxing' und 'unboxing' und wieso man es möglichst vermeiden soll?  #

In .NET gibt es Wertetypen und Referenztypen (siehe Blog-Eintrag "Struct oder doch lieber Klasse?"). Aber was soll passieren, wenn man einem Refernztypen den Wert eines Wertetypen zuweisen möchte?

Mit 'Boxing' versteht man die Umwandlung eines Wertetyps in einen Referenztypen und 'Unboxing' logischerweise die Umwandlung eines Referenztyps in einen Wertetyp.

Wozu ist das gut und wie "mache" ich 'Boxing'/'Unboxing'?

Schauen wir uns folgenden Code an:

ArrayList numbers = new ArrayList();

int i = 42;
numbers.Add(i);

float f = 42f;
numbers.Add(f);

Dies kompiliert prima und auch bei der Ausführung läuft alles wie geplant. Beim Aufruf von "numbers.Add(i)" wird automatisch ein 'Boxing' durchgeführt, da Wertetypen nicht in einer ArrayList gespeichert werden können sondern nur Referenztypen, die von System.Object abgeleitet sind.

Hier sieht man auch das große Problem: 'Boxing'/'Unboxing' geschieht automatisch ohne dass man etwas machen muss. Dies verleitet zu der obigen unsauberen Programmierung mit den entsprechenden Performanznachteilen, die eine automatische Umwandlung mit sich bringt.

Entsprechend wird hier ein automatisches 'Unboxing' gemacht:

int i2 = (int)numbers[0];

In .NET 2.0 wird 'Boxing'/'Unboxing' durch die Einführung von "Generics" verhindert. Durch den folgenden Code können zur Liste nur Font-Objekte hinzugefügt werden, so dass 'Boxing'/'Unboxing' unnötig ist:

List<Font> fonts = new List<Font>();

Drittes HowTo-Dokument zum MapPoint Web Service  #

Im dritten HowTo-Dokument zur Nutzung des MapPoint-Webservice in eigenen Anwendungen zeigen wir, wie man das geografische Informationssystem um einen Ereigniskalender erweiteren kann. Neben der Benutzung des Calendar-Controls von ASP.NET zeigen wir auch, wie man "Stored Procedures" bei datenbankbasierten Anwendungen sinnvoll einsetzt, um Anwendungscode und datenbankspezifischen Code voneinander zu trennen. Dieses HowTo-Dokument und auch die vorhergehenden HowTos zum MapPoint Web Service sind im Download-Bereich verfügbar.

Friday, July 28, 2006

Struct oder doch lieber Klasse?  #

Häufig werde ich gefragt, ob es „objektive“ Kriterien gibt, die eine Favorisierung von Structs gegenüber Klassen begründen.

Bislang habe ich auf die Frage nach einer Entscheidung zwischen Structs und Klassen geantwortet, dass dies von den genauen Umständen abhängt (also welche Aufgaben der zu implementierende Typ übernehmen soll).

Aber um die Antwort auf diese Frage zu verstehen, sollte man sich zu aller erst den wesentlichen Unterschied zwischen einer Struktur und einer Klasse verdeutlichen: während Klassen in C# (genau wie in allen anderen .NET-Sprachen) einen Referenztypen darstellen, die im Speicher auf dem Heap verwaltet werden, sind Structs Wertetypen, die auf dem Stack verwaltet werden. In C# werden primitive Datentypen wie z.B. Zahlenwerte als Wertetypen auf dem Stack verwaltet. Dies bedeutet, dass diese die Daten direkt enthalten, während Referenztypen lediglich einen Verweis auf die Daten enthalten. Da bei der Speicherung von Wertetypen keine weiteren Speicherreferenzen benötigt werden, können Strukturinstanzen im Allgemeinen ressourcenschonender als Klasseninstanzen eingesetzt werden.

 

Im operativen Einsatz macht sich die Unterscheidung zwischen Referenz- und Wertetypen u.a. bei einer Zuweisung (etwa beim Kopieren von Objekten) bemerkbar:

weist man einer Stuct-Variable A eine zweite Struct-Variable B zu, werden alle der Variablen B inhärenten Daten dupliziert und als Variable A auf dem Stack abgelegt (diese Daten existieren dann doppelt im Speicher). Weist man hingegen einer Klassen-Variable A eine zweite Klassen-Variable B zu, wird auf dem Stack lediglich eine zweite Referenz angelegt, die auf die gleiche Stelle im Heap zeigt wie die Variable B (in diesem Fall werden die Daten also nicht dupliziert, sondern existieren nur einmal im Speicher!). In diesem Fall würden Änderungen an den Heap-Daten beide Referenzen (A und B) auf dem Stack beeinflussen.

Die Unterscheidung von Referenz- und Wertetypen bedingt den weitereren Unterschied, dass einige der klassischen objektorientierten Konzepte wie z.B. Vererbung von Structs nicht unterstützt werden.

 

Nun aber wieder zurück zu der Frage nach den Kriterien zur Strategieauswahl: das MSDN hat in einem Artikel Struct Usage Guidelines“ die folgenden Punkte aufgeführt, wann Typen als Strukturen implementiert werden sollten (sinngemäß übersetzt):

  • sie agieren wie primitive Datentypen
  • ihre Instanzgröße liegt unter 16 bytes
  • sie sind unveränderlich
  • eine Wertetypsemantik ist erwünscht

Vor der Entscheidung, einen Typen als Struktur zu implementieren, sollte daneben auch bedacht werden, dass Structs natürlich nur als Werte einer Methode als Parameter übergeben werden können und nicht als Referenzen. Insbesondere bei häufigen Parameterübergaben sollte demnach aus Performance-Gründen eine Klassen-Implementierung favorisiert werden.

Webparts für Windows Sharepoint Services  #

Webparts stellen eine elegante Möglichkeit dar, die Entwicklung von Webanwendungen zu modularisieren. Zudem können sie innerhalb von Microsoft Sharepoint Portal Server 2003 dazu genutzt werden, zusätzlich zu den schon vorhandenen Komponenten zur Darstellung von Dokumenten, Listen, Aufgaben, Ereignissen, usw. weitere Komponenten verfügbar zu machen.

In einem HowTo zeigen wir Ihnen, wie Sie ihre eigenen Webparts entwickeln können. In der Beispielanwendung entwickeln wir ein Webpart, das Daten aus einer Datenbank extrahiert und auf der Webseite anzeigt. Das Webpart ist vielseitig einsetzbar, die SQL-Anfrage und Datenbankverbindung kann über Einstellungen konfiguriert werden. Der Source-Code des Webpart ist ebenfalls verfügbar.

Tuesday, July 25, 2006

Zweites HowTo-Dokument zu MapPoint verfügbar  #

Das zweite HowTo-Dokument zur Nutzung des MapPoint-Webservice in eigenen Anwendungen ist nun im Download-Bereich verfügbar. Nachdem wir in einem ersten HowTo die notwendigen Schritte zur Vorbereitung der Daten beschrieben hatten, beschreiben wir in diesem Dokument den Kern der Anwendung, d.h. die Nutzung der Webservices zum Erzeugen von Karten und Routenberechnung. Als Beispiel dient wieder die Informatiklandkarte, in der die Informatikforschungseinrichtungen erfasst sind.

Monday, July 24, 2006

Unterschied zwischen 'readonly' und 'const'  #

Auf den ersten Blick sehen die beiden Ausdrücke aus, als ob sie das ähnliche wenn nicht sogar das gleiche bewirken. Dem ist aber nicht so, es gibt kleine aber feine Unterschiede!

Unterschied 1: Ein beliebiger Typ kann 'readonly' gesetzt werden!

Folgendes Statement wird reibungslos kompilieren:

protected readonly Font readonlyFont = new Font("Arial", 12.0f);

Wohingehend hier ein Fehler auftritt:

protected const Font constFont = new Font("Arial", 12.0f);

'const' ist nur zulässig für primitive Datentypen, enums und Strings. 'readonly' ist universell einsetzbar.

Unterschied 2: 'const' ist "gefährlich" wenn man Librarys in Form von DLLs erstellt.

Das wird dadurch verursacht, wie der Compiler 'readonly' und 'const'-Typen behandelt.

'readonly'-Typen werden mit Namen und Wert in dem Assembly abgelegt, aber es ist nicht möglich, dass der Wert programm-technisch verändert wird.

'const'-Typen werden zur Kompilierzeit durch den Wert ersetzt! D.h. folgende beiden Varianten ergeben dasselbe Kompilat:

Variante 1:

protected const String helloWorld = "Hello World";

public void printHelloWorld() {
   Console.Out.WriteLine(helloWorld);
}

Variante 2:

public void printHelloWorld() {
   Console.Out.WriteLine("Hello World");
}

Und jetzt wird auch offensichtlich, wieso 'const' in DLLs "gefährlich" sind. Wenn ein Programmierer den Unterschied nicht kennt und ein Programm aus mehreren Assemblys zusammensetzt, werden in allen verwendeten Assemblys die konstanten Werte in die IL eingefügt. Ändert man den Wert und ersetzt eine von mehreren alten Assemblys durch eine neu-kompilierte, verwendet nur die neue Assembly den neuen Wert, die anderen Assemblys beinhalten ja noch den alten Wert!!

Dies kann nicht passieren bei der Verwendung von 'readonly', da dort immer eine Referenz auf die Definition gespeichert wird und somit überall derselbe Wert verwendet wird.

Friday, July 21, 2006

Formel-Analyse durch reguläre Ausdrücke in C#  #

In einem aktuellen Software-Projekt haben wir uns mit einer interessanten Aufgabenstellung beschäftigt, die so oder in ähnlicher Form mit Sicherheit dem ein oder anderen bekannt vorkommen dürfte: Parsen von benutzerdefinierten Formeln nach einer deklarierten Syntax.

Die zu analysierenden Formeln waren wie folgt aufgebaut:

function1(param1,function2(param1,param2),param3)

Jede Funktion der Formel besteht aus einem Keyword (im Beispiel function1 und function2), das die eigentliche Funktion identifiziert. Eine durch Kommata getrennte Parameterliste der Funktion wird anschließend in einem Klammerausdruck angegeben. Die Funktionen können natürlich beliebig geschachtelt sein.

Die Aufgabenstellung bestand nun darin, die einzelnen Funktionen aus der Gesamtformel zu filtern um diese dann anschließend zu berechnen.

Reguläre Ausdrücke bieten sich an, ein solches Problem zu lösen.

.NET stellt im Namespace System.Text für die Arbeit mit regulären Ausdrücken die Klassen Regex zur Verfügung.

Eine erfolgreiche Strategie, um die geschachtelten Funktionen aufzulösen, besteht nun darin, nach beliebigen Funktionen zu suchen, die selbst keine anderen Funktionen mehr als Parameter besitzen (eine solche Funktion muss in der Formel existieren. Eine andere Strategie könnte z.B. zuerst die „innerste“ Funktion finden und berechnen).

Dazu lässt sich der folgende reguläre Ausdruck definieren, mit dem die gesamte Formel geparst wird:

 

Regex regex = new Regex("(function1|function2)[(][^(^)]*[)]");

 

Der erste Teil dieses regulären Ausdrucks definiert eine Liste mit Keywords von Funktionen, die bekannt sind und später berechnet werden sollen. Der zweite Teil sucht nach Klammerausdrücken, die selbst keine öffnenden und schließenden Klammern beinhalten.

Das folgende C#-Schnipsel zeigt, wie man solche Funktionen filtern kann:

 

using System.Text.RegularExpressions;

….

string s = "function1(param1,function2(param1,param2),param3)";

Regex regex = new Regex("(function1|function2)[(][^(^)]*[)]");

MatchCollection mc = regex.Matches(s);

for (int i=0; i < mc.Count; i++){

   Console.WriteLine("Funktion an Pos.: " + mc[i].Index);

   Console.WriteLine("Funktion: " + mc[i].Value);

   computeValueOfFunction(mc[i].Value);

  }

 

Im Beispiel liefert mc[0].Value z.B. function2(param1,param2).

Hat man erst einmal eine Funktion identifiziert, kann man diese nach der Ermittlung ihrer Parameter (die durch Kommata getrennt sind und zwischen den Klammern stehen) und des eigentlichen Funktions-Keywords (der linke Teil vor der öffnenden Klammer) berechnen (im Beispiel angedeutet durch computeValueOfFunction(). Anschließend wird diese Funktion in der Formel durch ihr Ergebnis ersetzt und die so modifizierte Formel erneut geparst. Dies wird solange wiederholt, bis alle Funktionen berechnet wurden und die verbleibende Formel-Zeichenkette das Gesamtergebnis des Ausdrucks darstellt.

Monday, July 17, 2006

Datenzugriff der nächsten Generation  #

Heute wurde bei MSDN ein interessanter Artikel veröffentlicht, der einen guten Einblick in die neuen Features von ADO.NET 3 gibt. Den Artikel gibt es sowohl in Deutsch als auch in Englisch.

In ADO.NET 3 wird es möglich sein, die Anfragen für den Datenbankzugriff direkt im Programmcode zu definieren, d.h. die Anfragen an die Datenbank sind dann nicht mehr einfache Strings, die SQL-Befehle enthalten, sondern Anweisungen in der jeweiligen Programmiersprache (z.B. C# oder VB.NET). Dieses Prinzip erinnert ein wenig an Embedded SQL, hat aber einen entscheidenden Unterschied: die Ergebnisse werden im objekt-orientierten Modell der Anwendung zurückgegeben und nicht als relationale Tupel, die man noch mit einem Cursor auslesen muss. Dieses Prinzip hat zwei Vorteile:
  • Die Anfragen werden Teil des Programms und werden somit durch den Compiler z.B. auf Typkorrektheit geprüft. Beim klassischen Ansatz, dem Verwenden von SQL-Befehlen in Strings, kann es leicht zu Inkonsistenzen zwischen SQL-Befehlen und Programmiercode kommen, da die SQL-Befehle für den Compiler nicht transparent sind.
  • Der "Impedance Mismatch" zwischen Datenbanksystem und Programmiersprache wird aufgehoben. Normalerweise muss man die Ergebnisse der von SQL-Anfragen aufwändig in entsprechende Objekte der Anwendung umformen. Diese Transformationen machen häufig einen großen Teil des Anwendungscode aus und müssen bei Änderungen des Objektmodells der Anwendung oder des Datenbankschemas konsistent weiterentwickelt werden.
Durch diese objekt-orientierte Sicht sieht man das Datenbanksystem in einem Modell, das eher dem konzeptuellen Modell entspricht, das man während des Entwurfs benutzt hat (also z.B. dem Entity-Relationship-Modell). Dies ist eine "natürlichere" Sichtweise als die klassische logische Sicht, in der man die Datenbank nur als eine Ansammlung von Tabellen sieht.

Für mich als Datenbank-Forscher ist natürlich auch interessant, dass diese Neuerung direkt aus Datenbankgruppe von MS Research kommt und somit eine Anwendung aktueller Forschungsergebnisse ist.

In den nächsten Wochen werde ich hier über weitere Features des neuen ADO.NET berichten und auch einige technischen Details ansprechen.

Friday, July 14, 2006

Praktikum erfolgreich beendet  #

Das vom ZfS Aachen an der RWTH Aachen angebotene Praktikum zur Entwicklung von Webservices mit .NET wurde heute mit den Abschlusspräsentationen der vier Gruppen beendet. Im Laufe des Semesters mussten die Gruppen zunächst mehrere kleinere Webservices erstellen, um Daten über Bücher und wissenschaftliche Publikationen aus vorhandenen Datenquellen extrahieren zu können. Dazu wurden auch Benutzerschnittstellen erstellt, mit denen man mit einer Anfrage auf verschiedene Datenquellen zugreifen kann (z.B. Amazon, Datenbanken unserer Hochschulbibliothek, CiteSeer).

Im zweiten Teil des Praktikums mussten die Gruppen jeweils eine größere Projektaufgabe bearbeiten. Eine Gruppe hat sich zum Beispiel mit der Modellierung von Geschäftsprozessen und der Steuerungen von Webservices mit Microsoft BizTalk beschäftigt. Die Gruppe hat exemplarisch einen Bestellprozess für Bücher in BizTalk abgebildet und mit Webservices realisiert. Eine weitere Gruppe hat sich mit UDDI und dem dynamischen Aufrufen von Webservices befasst. Neben der Interaktion mit dem UDDI-Server war hier vor allem die Schwierigkeit der Vereinheitlichung der Schnittstellen der zuvor erstellten Webservices. Aufgabe der dritten Gruppe war die Erstellung eines webbasierten Dokumentenmanagementsystems, mit dem Dokumente (insbesondere wissenschaftliche Publikationen) verwaltet und annotiert werden können. Hierbei wurden auch die zuvor erstellten Webservices zur Suche von Dokumenten integriert. Schließlich war es Aufgabe der letzten Gruppe, ein Konferenzmanagementsystem zur Durchführung von wissenschaftlichen Tagungen mit Hilfe von Webservices zu erstellen. Dabei wurden die Web Service Enhancements 3.0 (WSE) benutzt, um eine sichere Kommunikation zwischen Webservices zu ermöglichen.

Die Ergebnisse des Praktikums waren durchweg sehr gut. Nicht nur die Studenten haben viel gelernt, sondern auch wir vom ZfS Aachen haben an einigen Stellen einen tieferen Einblick in die .NET-Technologie bekommen.

Mehr Details zu den Ergebnissen des Praktikums werden wir hier in den nächsten Wochen in Form von HowTos bzw. Webcasts veröffentlichen.

Wednesday, July 05, 2006

Neues Whitepaper online: das evidenzbasierte .NET Sicherheits-Konzept (CAS)  #

Das .NET-Framework erweitert das rollenbasierte Sicherheits-Konzept (so wie es z.B. bei vielen Betriebssystemen zum Einsatz kommt) um einen weiteren, beweislastorientierten Ansatz. Dieser neue Ansatz sammelt zur Laufzeit Informationen rund um die auszuführende Anwendung (z.B. ihre Herkunft) und übergibt der Anwendung dann auf der Grundlage dieser gesammelten Informationen und den Sicherheitseinstellungen des Systems die benötigten Rechte oder verweigert die Vergabe.

Dieses Whitepaper erläutert das Sicherheitskonzept des .NET-Frameworks und zeigt, wie und in welchem Umfang die Sicherheitseinstellungen konfiguriert werden können und worauf bei der Entwicklung eines Assemblies in diesem Zusammenhang zu achten ist, um böse Überraschungen bei der Ausführung zu vermeiden.

Win32-Systemaufrufe in .NET Teil 2: Fehlercode ermitteln  #

Dieser Beitrag ist eine Ergänzung zum Beitrag vom 29.06.06 „Win32-Systemaufrufe in .NET“, in dem ich gezeigt habe, wie .NET-Assemblies nicht verwaltete Funktionen sehr einfach über PInvoke (am Beispiel eines Win32-API-Aufrufs über das Attribut DLLImport) aufrufen können.

Ein weiterer interessanter Punkt, den ich in diesem Beitrag offen gelassen habe, ist die Frage, wie man auf Fehler, die eine solche nicht verwaltete Funktion möglicherweise erzeugt, reagieren kann. Eine erste, vielleicht nahe liegende Idee, einen potentiellen Fehler in einem try…catch-Block zu behandeln, wird erfolglos bleiben, da dieses Konstrukt nur verwaltete Fehler behandelt.

Das DLLImport-Attribut verfügt aber über einen Parameter SetLastError, der in diesem Fall helfen kann. Wird dieser Parameter auf true gesetzt, kann der Code des Fehlers, der von der unverwalteten WIN32-API-Funktion erzeugt wird, über die statische Methode Marshal.GetLastWin32Error() ermittelt werden. Betrachten wir das Beispiel-Fragment des letzten Beitrags und erweitern es um die hier erläuterten Möglichkeiten, erhalten wir folgendes Codeschnipsel:

 

using System.Runtime.InteropServices;

...

    class APICall

    {

        [DllImport("kernel32.dll", SetLastError= true)]

       

        static extern uint WinExec(string command, uint uCmdShow);

 

        public void callWinAPI(string file)

        {

            WinExec("Notepad.exe " + file, 1);

            int errorCode = Marshal.GetLastWin32Error();

            Console.WriteLine("Fehlercode: " + errorCode);

         }

    }

 

Um die Wirksamkeit der Fehlercode-Ermittlung zu prüfen, können wir der WinExec-Funktion nun einen Anwendungspfad oder -namen übergeben, der nicht existiert (z.B. „Notepads.exe“). Dies wird einen Fehler erzeugen, der als „errorCode“ gespeichert wird. Im Folgenden kann dann in Abhängigkeit des Fehlercodes eine Ausnahmebehandlung stattfinden.

Die nicht verwaltete Funktion FormatMessage aus der kernel32.dll liefert übrigens eine Textbeschreibung der entsprechenden Fehlercodes.

Eine detaillierte Übersicht der DLLImport-Attribute wird auf den MSDN-Seiten dargestellt.

 

Tuesday, July 04, 2006

Neuer Download-Bereich  #

Es ist wieder viel passiert: In den letzten Wochen waren unsere Experten für Softwarekonzepte sehr aktiv und haben zahlreiche HowTo-Dokumente, White Papers, Beispielanwendungen und Fallstudien für Sie geschrieben. Damit Sie den Überblick behalten, haben wir unseren Download-Bereich neu strukturiert. Sie finden ab sofort zu jedem Dokument einen übersichtlichen Tabelleneintrag mit Titel, Erscheinungsdatum und Kurzfassung. Wenn Sie mehr über die jeweiligen Autoren wissen wollen, dann kommen Sie über den Autorennamen sofort zu einer Kurzvita.

Bitte beachten Sie: Während die Kurzfassungen der Dokumente frei einsehbar sind, müssen Sie sich kostenlos bei uns registrieren, wenn Sie die Vollversionen unserer Dokumente herunterladen möchten.

Wir wünschen Ihnen viel Spaß beim Lesen und gutes Gelingen für das Umsetzen der Konzepte in Ihren eigenen Anwendungen. Über Ihre Rückmeldung würden wir uns natürlich sehr freuen.

Archiv

© 2008 Fraunhofer FIT - Powered by: newtelligence dasBlog 1.8.5223.2 - Sign In

Feeds: RSS 2.0 | Atom 1.0