86

Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

  • Upload
    vanphuc

  • View
    222

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

.Net Framework und C#

SS 2011

Teil I

Dr. Ute Blechschmidt-Trapp

Darmstadt, Darmstadt, 4. April 2011

Page 2: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

Inhaltsverzeichnis

1 Organisation 31.1 Praktikum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 Leistungsnachweis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Einleitung 52.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2 Literatur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3 Ziele der Veranstaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.4 .Net Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.4.1 Assembly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.4.2 Anwendungsdomäne (AppDomain) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.4.3 Basisklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.5 Visual Studio 2010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.5.1 Allgemeine Hinweise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.5.2 Debugger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.5.3 Refactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.5.4 UML-Diagramme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.5.5 Coding Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.5.6 Code-Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.5.7 XML-Dokumentationskommentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.5.8 Unit-Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2.6 P1 - VS 2010: Einrichten und freies Erkunden der Arbeitsumgebung . . . . . . . . . . . . . . . . 14

3 C# kompakt 163.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

3.1.1 is vs as . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193.1.2 Operatoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.1.3 Partial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

3.2 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.2.1 CTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203.2.2 Strukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223.2.3 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.2.4 Generika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.2.5 Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253.2.6 Enumerationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3.3 Attribute und Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.3.1 Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.3.2 Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Zugri� auf Attribute mit Re�ektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.3.3 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.3.4 IList, IEnumerable und andere Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

IComparable und IComparer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31IDisposable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

3.4 Sprachbesonderheiten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363.4.1 Log/Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373.4.2 Ausnahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373.4.3 Indexer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

3.5 Serialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.6 P2 - C# kompakt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443.7 Delegates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

3.7.1 Generische Delegates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473.7.2 Mulitcastdelegaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493.7.3 Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

2

Page 3: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

Inhaltsverzeichnis

3.7.4 Asynchrone Delegates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543.7.5 LINQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553.7.6 Erweiterungsmethoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563.7.7 Abfragen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

3.8 P3* - Delegates und Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583.8.1 Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

4 O�ce-Entwicklung 594.1 O�ce-Dokumente von auÿerhalb verändern oder erstellen . . . . . . . . . . . . . . . . . . . . . . 594.2 Excel-Addin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

5 Datenbankanbindung 625.1 .NET Framework-Datenanbieter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625.2 DataSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635.3 DataSet oder DataReader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645.4 P4* - Datenbanken und Addins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

6 Windows Presentation Foundation 676.1 XAML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676.2 Architektur von Windows Presentation Foundation und das Programmiermodell . . . . . . . . . 676.3 Steuerelemente und Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

6.3.1 Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696.3.2 Logischer Baum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716.3.3 Visueller Baum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716.3.4 Routed Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726.3.5 Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

6.4 Praktikum P5* - WPF Ober�äche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766.5 Datenbindung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

6.5.1 DependencyProperty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776.5.2 INotifyPropertyChanged . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776.5.3 Binding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786.5.4 DataContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

6.6 MVVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806.7 Praktikum P6* - WPF Datenbindung, Audio-Player . . . . . . . . . . . . . . . . . . . . . . . . . 846.8 Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

3 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 4: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

1 Organisation

Die Veranstaltung wird mit 2+2 SWS angeboten und ist mit 5 CPs bewertet. Ich setzte Kenntnisse der objek-torientierte Programmierung, Datenbank- und Web-Entwicklung voraus.

1.1 Praktikum

Das Praktikum ist in zwei Hauptblöcke geteilt. Im ersten Block bearbeiten Sie Aufgaben des Skripts, entwickelndabei einen einfachen Audio-Player und geben diese ab. Im zweiten Block erstellen Sie eine von Ihnen gewählteund ein konkretes Problem lösende (also nützliche) .Net-Applikation. Auÿerdem arbeiten Sie sich selbständig inein in der Vorlesung nicht behandeltes .Net-Thema ein (Zusatzthema). Sie können das Zusatzthema gerne mitIhrem Projekt kombinieren. Termine mit Anwesenheitsp�icht sind mit einem * markiert.

• 29.3.2011 - P1* - VS 2010: Einrichten und freies Erkunden der Arbeitsumgebung, AnforderungsanalyseAudio-Player, Aufgaben siehe Abschnitt 2.6 auf Seite 14, Abgabe per Mail bis zum 03.04.2011

• 05.04.2011 - P2 - C# kompakt: Programmieraufgaben und Entwurf Audio-Player, Aufgaben siehe SeiteAbschnitt 3.6 auf Seite 44, Abgabe: 10.04.2011

• 12.04.2011 - P3* - Delegates: Aufgaben siehe Abschnitt 3.8 auf Seite 58, Abgabe: 17.04.2011

• 19.04.2011 - P4* - Excel-Addin, Datenbankanbindung: Aufgaben siehe Abschnitt 5.4 auf Seite 66, Abgabe:01.05.2011

• 03.05.2011 - P5* - WPF-Ober�äche: Aufgaben siehe Abschnitt 6.4 auf Seite 76, Abgabe: 08.05.2011

• 10.05.2011 - P6* - WPF Datenbindung und MVVM: Aufgaben siehe Abschnitt 6.7 auf Seite 84, Abgabe:15.05.2011

• 17.05.2011 - P7 - Ihr Projekt: Projektbeschreibung, Entwurf (Abgabe: 22.05.2011)

• 24.05.2011 - P8* - Ihr Projekt: Implementation der Grundfunktionalität (Abgabe: 29.05.2011)

• 31.05.2011 - P9 - Zusatzthema: Zusammenfassung geeigneter Quellen und Implementation einer Demo-Applikation (Abgabe: 05.06.2011)

• 07.06.2011 - P10* - Zusatzthema: Blogartikel, Finalisierung Projekt und Zusatzthema: Abgabe 19.06.2011

• 21.06.2011 - P11 - Präsentation der Projekte, Abgabe der Projektmappe bis zum 24.06.2011

Projekt. Im Projekt sollen Sie eine für sich nützliche Anwendung mit momentan empfohlenen Methoden inC# implementieren. Inhalt und Umfang stimmen Sie in P6 mit mir ab. Sie präsentieren/demonstrieren IhrProjekt den anderen Teilnehmern der Veranstaltung.

Zusatzthema. Sie arbeiten sich selbständig in ein in der Vorlesung nicht behandeltes Thema (z.B. XNA,Crystal Reports, MEF, EF, Silverlight) ein. Sie erstellen eine Demo-Applikation und schreiben einen Blogartikel,der für Sie nützliche Quellen und Codefragmente enthält. Diesen Blogeintrag werde ich ggf. überarbeiten under kann anonym oder in Ihrem Namen auf meinem Blog (bletra.wordpress.com) verö�entlicht werden.

Abgabe. Während des Semesters nehme ich zu den in obiger Liste gekennzeichneten Terminen Teile desPraktikums ab. Die Deadline geht jeweils bis 23:59 Uhr des angegebenen Datums. Sie können die Aufgabenin Zweiergruppen bearbeiten. Programmcode geben Sie bitte als Zip und Essay-Aufgaben als PDF ab. Zurkontinuierlichen Verbesserung der Veranstaltung können Sie mir mit einem kurzen Feedback helfen: Wie langehaben Sie zur Bearbeitung benötigt? Wo hatten Sie ein Aha-Erlebnis? Welche Aufgaben waren eher langweilig?

Zusätzlich geben Sie zu Semesterende alle von Ihnen bearbeitete Aufgaben (inklusive Projekt und Zusatzthema)in Form einer Praktikumsmappe ab.

4

Page 5: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

1.2 Leistungsnachweis

1.2 Leistungsnachweis

Der Leistungsnachweis erfolgt durch eine Klausur am Semesterende. Der Termin und der Raum werden imPrüfungsterminplan festgelegt.

Der Inhalt richtet sich nach der Vorlesung im SS 2011. Die Klausur wird aus Programmieraufgaben � die sichan den Aufgaben im ersten Teil des Praktikums orientieren � und aus Verständnisfragen � die sich auf in derVorlesung behandelte, aber im Praktikum nicht notwendigerweise vertiefte Themen beziehen � zusammenset-zen.

Das Praktikum besteht aus zwölf Teilaufgaben. Zu jeder Teilaufgabe ist eine Lösung per Mail einzureichen.Im Praktikum werde ich stichprobenartig Fragen zu den abgegebenen Lösungen stellen. Jede Lösung wird vonmir insgesamt mit einem �ok� bzw. �nicht ok� bewertet. Für das Testat des Praktikums müssen Sie folgendeBedingungen erfüllen

• zu allen mit * gekennzeichneten Terminen pünktlich erscheinen,

• für P1-P6 wenigstens für drei Praktika ein �ok� erhalten,

• das Projekt und Zusatzthema (mit Blogbeitrag) bearbeiten und präsentieren und

• eine Praktikumsmappe abgeben.

Zusätzlich können Sie Bonuspunkte für die Klausur am Semesterende SS2011 erwerben:

• 12 �ok�: 6 Bonuspunkte

• 11 �ok�: 5 Bonuspunkte

• 10 �ok�: 4 Bonuspunkte

• 9 �ok�: 3 Bonuspunkte

• 7 �ok�: 2 Bonuspunkte

• 5 �ok�: 1 Bonuspunkt

Eine Übertragung der Bonuspunkte ins nächste Jahr wird nicht gewährleistet.

5 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 6: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2 Einleitung

2.1 Motivation

Warum biete ich dieses Wahlfach an? Ich habe 10 Jahre bei einem zerti�zierten Partner von Microsoft gearbeitetund dabei Microsofts Entwicklungsumgebung und die Möglichkeiten des .Net-Frameworks kennen gelernt. Dieverschiedenen aktuellen Technologien von Microsoft sind mächtig und es macht Spaÿ, mit einer komfortablenIDE zu arbeiten.

Leider oder zum Glück gibt es im .Net Umfeld ca. alle 2 Jahre eine neue Version. Glück für die Entwicklerinnen,die stumpfsinniges Arbeiten durch mächtige Werkzeuge und Bibliotheken weiter abgenommen bekommen undleider, da sich die Prinzipien dadurch auch grundlegend ändern können. Was ist also das bleibende Wissen,das in einer Hochschulveranstaltung vermittelt werden soll? Für Entwicklerinnen, die bereits eine OO-Sprachebeherrschen, kann dies nicht nur das Erlernen der Sprache C# sein. Das Erlernen einer weiteren OO-Spracheist keine Herausforderung. C# und Java sind sehr ähnlich. In dieser Veranstaltung werden wir uns also auf dieUnterschiede konzentrieren. Der Fokus dieser Vorlesung liegt auf . . .

• dem Arbeiten mit VS2010,

• die Verwendung ausgewählter Klassen des .Net Frameworks

• den sprachlichen Besonderheiten von C#

• wo �nde ich gute Informationen zu momentan empfohlenen Vorgehensweisen und wesentlichen Technolo-gien?

• Re�ektion der Möglichkeiten und Grenzen des Frameworks

• Methoden zur Analyse von generierten Code und des Frameworks an sich

• Ihren Fragen

Das Skript enthält die Praktikumsaufgaben und Fragen. Ein Teil der Fragen dient dazu, Recherchen und Dis-kussionen anzuregen. Diese Fragen können nicht notwendigerweise eindeutig beantwortet werden. Ein andererTeil der Fragen eignet sich zur Selbstkontrolle oder auch zur Klausurvorbereitung.

Dies ist eine Beta-Version des Skripts. Informieren Sie mich bitte über Fehler, Unklarheiten, Lücken, machen SieVorschläge für eine bessere Struktur, verständlichere Formulierungen, Übungsaufgaben, Schaubilder. Ich freuemich über jede konstruktive Kritik.

Teil 2 des Skripts werde ich Ihnen zeitnah zur Verfügung stellen.

2.2 Literatur

Es gibt viele dicke Bücher zu C#, Asp.Net und anderen Themen dieser Vorlesung. Es gibt meiner Meinungnach wenige gute Bücher, die für Quereinsteiger von anderen OO-Sprachen geschrieben werden, so dass Sie vielGeld für Gedrucktes, dass Sie aus anderen Veranstaltungen kennen, ausgeben. Dies, die Schnelllebigkeit vonMicrosoft-Technologien und die guten Online-Materialien führen zu folgender Linkliste:

• Microsoft

� MSDN � Das Microsoft Developer Network http://msdn.microsoft.com

� Channel9 � Videos und anderes Lernmaterial http://channel9.msdn.com/learn/

� patterns & practices: http://msdn.microsoft.com/en-us/practices/default.aspx

� Microsoft Application Architecture Guide, 2nd Edition http://msdn.microsoft.com/en-us/

library/dd673617.aspx

� Magazin http://msdn.microsoft.com/en-us/magazine/default.aspx

6

Page 7: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.3 Ziele der Veranstaltung

� Visual Studio 2010 and .NET Framework 4 Training Kit http://www.microsoft.com/downloads/details.aspx?familyid=752CB725-969B-4732-A383-ED5740F02E93&displaylang=en

� .Net Quellcode: http://referencesource.microsoft.com

• Developer Blogs

� http://blogs.msdn.com/

� http://weblogs.asp.net/

• Code

� Codeproject http://www.codeproject.com/

� Codeplex http://cfx.codeplex.com

� Design Patterns http://www.dofactory.com

� Code-Snippets: http://dotnet-snippets.de

2.3 Ziele der Veranstaltung

Aus dem Modulhandbuch: Die Studierenden lernen, prinzipielle Probleme und Aufgaben in verschiedenen An-wendungsgebieten nach momentan empfohlenen Methoden mit C# zu lösen. Lehrinhalte:

• Arbeiten mit der IDE Visual Studio

• Datenbankanbindung

• Webentwicklung

• Desktopentwicklung

• Komponentenentwicklung

Wie eingangs erwähnt, werden nicht alle momentan diskutierten .Net-Hypes aufgegri�en, sondern es geht um�best-practice� und die zugrundeliegenden Prinzipien.

2.4 .Net Framework

Lernziele:

• Grundlegende Arbeitsweise von .Net kennen lernen.

• Die Laufzeitumgebungen von .Net und Java vergleichen.

• .Net Vokabular lernen.

Das .Net-Framework und die zugehörige Laufzeitumgebung basieren auf dem Standard (ECMA-335). DieseInternationale Norm de�niert die Common Language Infrastructure (CLI), in denen Anwendungen in mehrerenHochsprachen geschrieben und in unterschiedlichen Systemen ausgeführt werden können. Diese Norm regeltfolgende Bereiche:

• Partition I: Konzepte und Architektur � Beschreibt die Gesamtarchitektur der CLI. Spezi�ziert dazudas Common Type System (CTS), das Virtual Execution System (VES) und die Common LanguageSpeci�cation (CLS).

• Partition II: Metadatende�nition und Semantik � Enthält Informationen über Metadaten: Das physischeLayout der Dateien, die logischen Inhalte und deren Struktur.

• Partition III: CIL � Beschreibt die Instruktionen der Common Intermediate Language (CIL).

• Partition IV: Bibliotheken � Enthält eine Spezi�kation von Klassen und Klassenbibliotheken, die als Teilder CLI standardisiert sind.

• Partition V: Beschreibt das einheitliche Debuggingformat.

7 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 8: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.4 .Net Framework

• Partition VI: Anhänge.

Abbildung 2.1 zeigt den Weg von C#-Programmcode zum ablau�ähigen Maschinencode.

Abbildung 2.1: Vom C#-Quellcode zur Ausführung des Maschinencodes, Quelle: Microsoft

Mit einem Vorcompiler wird Programmcode in die Common Intermediate Language (CIL) (teilweise auch nurIntermediate Language (IL) genannt) übersetzt. CIL ist eine objektorientierte Assemblersprache, d.h. sie kenntkomplexe Datentypen und Objekte und Konzepte wie Vererbung und Polymorphie.

Das physikalische Ergebnis dieses Compilevorgangs sind die sogenannten Assemblies (siehe Abschnitt 2.4.1 aufSeite 8). Die Assemblies be�nden sich im lokalen Verzeichnis oder sind im Global Assembly Cache (GAC)registriert. Der Just in time compiler (JIT) erzeugt daraus sogenannten Managed Code, der in der CommonLanguage Runtime (CLR) für das jeweilige Betriebssystem umgesetzt wird. Managed Code ist per De�nitionCode, der von der CLR interpretiert wird. Jeder andere Code ist unmanaged. Unmanaged Code unterliegt alsonicht der Aufsicht der CLR, ist plattformabhängig und typischerweise ohne das .Net Framework erstellt. Derdurch den JIT-Compiler erstellte Maschinencode wird bei Beendigung des Prozesses, der die ausführbare Dateiausführt, verworfen. Daher muss die Methode beim erneuten Ausführen der Anwendung wieder neu kompiliertwerden. NGen erlaubt die Vorkompilierung von CIL-Anwendungen in Maschinencode vor der Ausführungszeit� typischerweise während der Installation.

Frage 2.1Entspricht die Verwendung von NGen einer herkömmlichen statischen Back-End-Kompilierung?

Frage 2.2Wodurch unterscheidet sich die IL von einer �reinen� Assemblersprache?

8 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 9: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.4 .Net Framework

Abbildung 2.2: .Net Framework, Quelle: Microsoft, TrainingKit

Merke 1 (Laufzeitumgebung)Der Zwischencode kann mit einem entsprechenden Vorcompiler auf Basis einer belie-bigen Programmiersprache erzeugt werden. Ein einheitliches Typsystem (CTS) (sieheAbschnitt 3.2.1 auf Seite 20) gewährleistet die gleichberechtigte Kommunikation zwi-schen verschiedenen Entwicklungssprachen.

Abbildung 2.2 zeigt wesentliche Klassenbibliotheken von .Net und die Hauptkomponenten der CLR.

2.4.1 Assembly

Ein Assembly ist . . .

• eine Menge von Typen, die zusammenkompiliert werden, inklusive Ressourcen wie Bilder.

• eine Laufzeiteinheit, die als Datei gespeichert wird (*.exe oder *.dll), auch portable Executable (PE)genannt.

• die kleinste Einheit in Bezug auf

� Deployment,

� dynamischem Laden,

� Sicherheit

� Versionierung

• enthält Code (objekorientierter Assemblercode)

• enthält Metadaten

• enthält ein Manifest (Inhaltsverzeichnis)

• ist vergleichbar mit einer Java JAR-Datei

• ist eine .Net Komponente

Man unterscheidet zwischen privaten und ö�entlichen Assemblies. Ö�entliche Assemblies werden im GAC re-gistriert, was zur Folge hat, dass sie über einen strong name verfügen müssen. Ein solcher Name ist eindeutig,ähnlich einer GUID. Assemblies im GAC stehen allen Applikationen zur Verfügung. Private Assemblies be�ndensich im Verzeichnis der Applikation, können nur von dieser Applikation verwendet werden und können nichtsigniert werden. Der GAC ist ein sicherer Speicher, der verschiedene Versionen einer Assembly enthalten kann.

Ein Strong Name setzt sich aus vier Teilen zusammen:

9 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 10: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.4 .Net Framework

• Dateiname, z.B. db.dll

• Versionsnummer, z.B. 1.1.123.25

• der Kultur (de-de, en-us, ...), Sprach und länderspezi�sche Information

• einem ö�entlichen Schlüssel

Die Metadaten enthalten den public key, Informationen über exportierte Typen (Name, Sichtbarkeit, Basisklasse,implementierte Interfaces, Members, Attribute), Abhängigkeiten zu anderen Assemblies und Sicherheitseinstel-lungen.

Frage 2.3Verscha�en Sie sich einen Überblick über die global installierten Assemblies auf Ihrer Maschine:

(a) Navigieren Sie im Windows-Explorer in das Verzeichnis Windwosassembly.

(b) Schauen Sie sich das gleiche Verzeichnis auf der Kommandozeile an.

Merke 2 (Assembly)Eine Assembly ist die kleinste Einheit in Bezug auf Deployment, dynamisches Laden,Sicherheit und Versionierung.

2.4.2 Anwendungsdomäne (AppDomain)

Anwendungsdomänen sind isolierte Bereiche im CLR-Prozess. Sie werden auch als leichtgewichtige Unterpro-zesse oder Pseudoprozesse bezeichnet. Wichtig ist, sie sind KEINE Prozesse! Innerhalb der CLR gibt es keineHardware-Kapselung. Anwendungsdomänen ...

• Schirmen Applikationen gegeneinander ab

• Umfassen (und isolieren) Assemblies, Module und Typen einer Applikation, Methodentabellen der gela-denen Typen und Statische Felder und Objekte der geladenen Typen

• Können entweder direkt im Code (System.AppDomain.CreateDomain) oder vom jeweiligen Host der CLRerzeugt werden.

Es gibt keine direkte Kommunikation zwischen Anwendungsdomänen. Es ist auch kein direkter Zugri�auf Assemblies anderer Anwendungsdomänen möglich. Die CLR verhindert die Übergabe von Objekt-Referenzen zwischen Anwendungsdomänen. Eine Interaktion ist nur über einen speziell gesicherten Inter-Domain-Kommunikationsmechanismus möglich.

Typischerweise arbeitet man nur mit einer Anwendungsdomäne. Mehrere Anwendungsdomänen sind erforder-lich, wenn Code mit anderen Sicherheits-Einstellungen/Zugri�srechten geladen wird, die Isolation zwischenCode-Teilen explizit gewünscht wird, oder Code unabhängig voneinander terminieren können soll. Für Plug-Insbeispielsweise bieten sich separate Anwendungsdomänen an, also eine AppDomain pro Plug-In. Das Stichwortzur Kommunikation zwischen verschiedenen Anwendungsdomänen ist Remoting.

Frage 2.4Wodurch unterscheidet sich ein Thread von einem Prozess und ein Prozess von einer AppDomain?

10 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 11: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.5 Visual Studio 2010

2.4.3 Basisklassen

Ein Namensraum ist ein logisches Benennungsschema zum Gruppieren von verwandten Typen. In einer Dateikönnen mehrere Klassen und mehrere Namensräume de�niert werden � dies erhöht sicher nicht die Übersicht.Namensräume und Klassen sind vom Verzeichnisbaum unabhängig! Mit dem Schlüsselwort using können in C#Namensräume in andere Namensräume importiert werden. Es ist auch möglich, Aliase zu vergeben. Namensräu-me sind hierarchisch organisiert. Mit using System werden alle ö�entlichen Typen von System importiert.Möchteman eine Klasse innerhalb von System verwenden, so ist diese in Bezug auf System voll anzugeben.

Die Basisklassen von .Net sind in solchen Namensräumen abgelegt. Die Poster �.Net Framework 4 and Exten-sions� oder �.Net Framework 4 Universe� veranschaulichen die aktuelle Struktur und Neuerungen.

Frage 2.5Beschreiben Sie in eigenen Worten die folgenden Begri�e und nennen Sie, wo möglich vergleichbare Elementein Java:

• Assembly

• CLR

• Namespace

• Anwendungsdomäne

• Strong Name

• GAC

2.5 Visual Studio 2010

Visual Studio 2010 ist eine kommerzielle Entwicklungsumgebung für .Net-Anwendungen. Über MSDNA könnenStudierende auch die Ultimate-Version kostenlos beziehen und gemäÿ den Lizenzbestimmungen nutzen. DieseVeranstaltung basiert auf VS2010 Ultimate. Die Express-Version ist eine abgespeckte freie Version. An dieserStelle möchte ich einige hilfreiche Vorgehensweisen und Shortcuts einführen. Erkunden Sie die Entwicklungs-umgebung und machen Sie sich mit ihr vertraut.

11 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 12: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.5 Visual Studio 2010

Abbildung 2.3: Refactoring, links VS2010, rechts mit Resharpener

2.5.1 Allgemeine Hinweise

Applikationen werden in Projektmappen verwaltet. Ein Projekt ist eine logische Einheit und erzeugt eine eigeneAssembly. Dies kann eine Consolen-Applikation oder auch ein Datenbankmodul sein. VS2010 bietet IntelliSen-se, Debuggen, Refactoring und vieles mehr. Sie können mehrere Projekte in einer �Solution� verwalten. MitRechtsklick auf ein Projekt können Sie dieses als Startprojekt festlegen.

Wie bei jeder IDE, gibt es auch in VS2010 eine Fülle an Shortcuts. Ein zweiseitiger Überblick bietet Microsoftauf der Seite �Visual Studio 2010 Keybinding Posters�. ich benutze häu�g folgende Shortcuts:

• F12: Zur De�nition einer Methode oder Klasse gelangen Sie, in dem Sie den Klassennamen z.B. bei derInstanziierung markieren und nun F12 drücken. Mit Ctrl+Shift+8 kommen Sie zurück in die Codezeile,von der Sie F12 gedrückt haben.

• Beim Debuggen: F11 (nächste Anweisung), Shift+F11 (aktuelle Methode beenden), F10 (Methode aus-führen, nicht Schritt für Schritt).

• Ctrl+w j: Der Objektkatalog bietet eine gute Übersicht über alle eigenen und referenzierten Objekte mitMethoden, Eigenschaften und Schnittstellen. Mit �Ansicht → Objektkatalog (Object Browser)� geht esauch.

2.5.2 Debugger

In dem Sie links in den grauen Bereich neben eine Codezeile klicken, können Sie Haltepunkte setzen. Dergrüne Pfeil startet den Debugger. Wenn Sie nun langsam mit der Maus über eine Variable gehen, sehen Sieden aktuellen Wert. Sie können auch eine Variable markieren, rechte Maustaste und �Add watch� wählen. ImFenster �Watch� sehen Sie die Werte überwachter Variablen. Sie können die Spalte �Value� auch während derAusführung editieren. Sie können beim Debuggen auch an jeder Stelle den gelben Pfeil anklicken und nach obenziehen und so einen kni�igen Teil erneut debuggen. Sie können sich mit dem Debugger auch an eine bereitsausgeführte Applikation anhängen. Dies benötigt man häu�g, um eigene Bibliotheken in anderen Applikationenzu debuggen, z.B. ein Add-In in Excel. Wählen Sie hierfür �Debuggen → An den Prozess anhängen (attach toprocess).

2.5.3 Refactor

VS 2010 bietet bereits einige hilfreiche Refactor-Werkzeuge, wie Umbenennen von Variablen, erzeugen vonProperties. Markieren Sie hierzu eine Variable, drücken Sie die rechte Maustaste und wählen Sie nun �Refactor�.Sie bekommen nun verschiedene Möglichkeiten des Refactorings angezeigt. Wer ein ausgefeilteres Refactoringwünscht, muss das Werkzeug Resharpener installieren � siehe Abbildung 2.3.

12 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 13: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.5 Visual Studio 2010

2.5.4 UML-Diagramme

VS2010 ermöglicht die Erstellung verschiedener UML-Diagramme, wie Anwendungsfälle, Sequenz-, Aktivitäts-,Komponenten- und Klassendiagramm. Diese Diagramme �nden Sie im Bereich Architektur. Die Diagrammegehören zu einem eigenen Projekttyp, dem Modelierungsprojekt. Per Rechtsklick können Sie aus dem Klassen-diagramm den zugehörigen C#-Code generieren.

Frage 2.6Bieten die VS-Diagramme die sonst üblichen Darstellungs-Möglichkeiten von UML? Was fehlt?

2.5.5 Coding Guidelines

Nach dem Naming Guideline von Microsoft sollen Parameter, Klassen etc. wie folgt benannt werden, Basis istPascalCase und camelCase:

• Nur Parameter und private Members sind camelCase

• Namespace:CompanyName.TechnologyName[.Feature][.Design], Beispiel: Microsoft.Media.Design

• Klassen: Nomen, Beispiel: class FileStream

• Methoden: Verben, Beispiel: RemoveAll()

• Interfaces: Nomen oder Nomenausdrücke, Beispiel: IServiceProvider, IFormatable

Diese Namensrichtlinien können Sie mit StyleCop validieren. Auf Assembly-Ebene analysiert das WerkzeugFxCop vorwiegend Designkonventionen für Entwicklerinnen von Klassenbibliotheken (Design Guidelines forClass Library Developers), aber auch einige Namenskonventionen.

Weitere Coding Guidelines �nden Sie unter:

• Coding style guideline used by the Microsoft All-In-One Code Framework project team.

• Coding Guidelines Projekt bei codeplex

Sie können natürlich auch einer anderen Richtlinie wie Zend für PHP folgen. Machen Sie dies in der Dokumen-tation Ihres Codes deutlich.

2.5.6 Code-Analyse

Zur Einhaltung von Coding-Guidelines und anderen Richtlinien können Sie bei den Projekteingenschaften unterder Rubrik �Build� die Option �Enable Code Analysis on Build� aktivieren. Eine Übersicht der dadurch über-prüften Code-Eigenschaften und zugehörigen Warnings liefert Code Analysis for Managed Code Warnings.

2.5.7 XML-Dokumentationskommentare

In Visual C# lässt sich eine Dokumentation des Codes erstellen. Dazu werden XML-Tags in spezielle Kom-mentarfelder des Quellcodes unmittelbar vor dem Codeblock eingefügt, auf den sie sich beziehen. Wenn Sie mit/doc kompilieren, sucht der Compiler alle XML-Tags im Quellcode und erstellt eine XML-Dokumentationsdatei.XML-Dokumentkommentare sind keine Metadaten und sind nicht in der kompilierten Assembly enthalten. Folg-lich ist der Zugri� auf sie über Re�ektion nicht möglich. Wenn Sie in der IDE /// vor einem Codeblock eingeben,erzeugt Ihnen VS automatisch einen passenden Kommentar. Möchten Sie zusammenhängende Methoden, z.B.ctor (Konstruktoren), private Methoden o.ä. einfach ein- und ausklappen können, so verwenden Sie #regionctor . . .#endregion. Mit Hilfe von Werkzeugen, wie z.B. Sandcastle - Documentation Compiler for ManagedClass Libraries oder Doxygen können Sie aus der XML-Datei eine lesbare Dokumentation erzeugen.

13 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 14: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.5 Visual Studio 2010

2.5.8 Unit-Tests

VS2010 bietet eine sehr einfache Möglichkeit, Unit-Test innerhalb von .Net zu erstellen. Microsoft bietet ein ei-genes Test-Framework (MSTest) mit direkter Integration in VS2010 an. Sie können beispielsweise in eine Klassegehen und mit Rechtsklick Unit-Tests erzeugen � diese müssen Sie natürlich noch entsprechend ausprogram-mieren. Die genauen Anweisungen für einen einfachen Testlauf können Sie dem Artikel Walkthrough: Creatingand Running Unit Tests entnehmen.

Die aus Java oder PHP bekannte Suite JUnit oder PHPUnit gibt es auch als .Net-Variante unter dem NamenNUnit. Es ist sicherlich sinnvoll, sich eines verbreiteten Frameworks zu bedienen, um sich nicht spezi�schen Be-sonderheiten eines ubiqutären Systems anzueignen. Die Assert-Api von NUnit ist umfangreicher als bei MSTest.Auÿerdem bietet NUnit eine gute Integration in �CruiseControl� und man kann eine Build-Umgebung unab-hängig von Microsofts Team Foundation Servers aufsetzten. Dies ist insbesondere für die Zusammenarbeit miteinem Java-Team wichtig. NUnit soll auch performanter als MS-Test sein, was insbesondere bei Groÿprojektenrelevant ist, da erst nach erfolgreichen Unit-Tests die Testabteilung anfangen kann.

Um NUnit direkt aus VS2010 nutzen zu können, emp�ehlt sich die Installation des Plugins TestDriven.Net oderdie Verwendung des bereits erwähnten ReSharpener.

Folgende Artikel bieten einen guten Vergleich zwischen MSTest und NUnit:

• http://osherove.com/blog/2010/3/5/nunit-vs-mstest-nunit-wins-for-unit-testing.html

• http://www.slideshare.net/orbitone/nunit-vs-mstest

• http://www.barebonescoder.com/2010/06/mstest-vs-nunit-with-visual-studio-2010-tdd/

Für diese Veranstaltung ist es Ihnen frei gestellt, welche Testumgebung Sie nutzen.

14 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 15: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.6 P1 - VS 2010: Einrichten und freies Erkunden der Arbeitsumgebung

2.6 P1 - VS 2010: Einrichten und freies Erkunden der Arbeitsumgebung

Abgabetermin: 03.04.2011

Aufgabe P1.1(Keine Abgabe) Starten Sie VS 2010. Erstellen Sie eine Console-Anwendung. Fügen Sie folgenden Code manuellin die Methode main ein:

Listing 2.1: Erstes Programm

Console.WriteLine("started ...");

string greeting="hi ";

Console.WriteLine(greeting);

for (int i = 0; i < 2; i++)

Console.WriteLine(i.ToString());

Console.WriteLine("press any key to stop");

Console.ReadLine();

(a) Schauen Sie sich die angebotenen Methoden bei Eingabe des Punktes an.

(b) Markieren Sie das Wort Console und drücken Sie F12.

(c) Führen Sie das Programm aus.

(d) Debuggen Sie das Programm, fügen Sie Haltepunkte ein, beobachten Sie einzelne Variablen, gehen Sieschrittweise weiter.

(e) Nutzen Sie Refactoring, um die Variable greeting umzubenennen.

Aufgabe P1.2Der Compiler erzeugt Ihnen eine Assembly.

(a) Wo be�ndet sich die Datei?

(b) Schauen Sie sich die Datei mit �ildasm.exe� (be�ndet sich in den SDK-Tools unter VS) an.

(c) Verändern Sie in der Datei AssemblyInfo.cs die Einträge für Company und Titel. Wie wirkt sich diesaus?

(d) Erzeugen Sie einmal eine Release-Version und einmal eine Debug-Version. Wodurch unterscheiden sich dieAssemblies für diese Kon�gurationen?

(e) Wählen Sie im Menü �Projekt� Ihre Projekteigenschaften aus. Wählen Sie nun �Ressourcen� und erstellenSie eine neue Ressource. Geben Sie nun ein Variable-Wert-Paar ein.

(f) Schauen Sie sich die erzeugte Datei Resources.Designer.cs an.

(g) Wie können Sie die eben erzeugte Ressource auf der Console ausgeben?

(h) Compilieren Sie das Projekt und schauen Sie sich erneut das erzeugte Assembly an.

(i) Welche Informationen werden in der Assembly in welcher Form gespeichert?

Aufgabe P1.3(Keine Abgabe) Erstellen Sie eine einfache WPF-Applikation, die bei Drücken eines Knopfes den Inhalt einesLabels verändert. Die Grundlagen hierfür sind eigentlich noch nicht gelegt, jedoch ist die Bedienung recht intuitivund Sie sollen bei dieser Aufgabe explorativ mit VS 2010 arbeiten.

Aufgabe P1.4In den Praktika P1-P6 entwickeln Sie schrittweise einen Audio-Player, dieser soll folgenden Anforderungengenügen:

• Es ist möglich, Playlists anzulegen, zu laden und abzuspeichern.

• Die Architektur soll es ermöglichen, Audio-, Video- und Bilddateien abzuspielen. Sie implementieren jedochnur einen Audioplayer.

• Eine Playlist, kann abgespielt, angehalten, beendet werden. Es ist möglich, die Lautstärke zu regeln undeine Playlist kann auch zufällig abgespielt werden.

• Zu einer Playlist können Audio-Dateien hinzugefügt oder entfernt werden.

15 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 16: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

2.6 P1 - VS 2010: Einrichten und freies Erkunden der Arbeitsumgebung

• Beim Start wird die zuletzt verwendete Playlist geladen, das Abspielen beginnt an der entsprechendenStelle.

• Sie können automatisch alle Audio-Dateien eines frei wählbaren Verzeichnisses hinzufügen. Dies erfolgtasynchron im Hintergrund.

Setzen Sie sich mit der Aufgabestellung auseinander. Identi�zieren Sie Inkonsistenzen oder o�ene Punkte undklären Sie die Fragen mit Ihrem Betreuer. Erarbeiten Sie eine Liste von kurzen und informellen Anwendungsfällen(User Stories d.h. Überschrift + ein bis zwei Sätze). Dokumentieren Sie zu jeder User Story mit mindestenseinem Test, wie Sie überprüfen wollen, dass die Funktionalität umgesetzt wurde. Erstellen Sie eine Skizze derAnwendungsober�äche.

16 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 17: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3 C# kompakt

Lernziele:

• Bisher bekannte OO-Elemente in C# wieder �nden.

• Sprachbesonderheiten kennen lernen, sinnvoll einsetzen können und mit Lösungsmodellen anderer Pro-grammiersprachen vergleichen.

• VS2010 sinnvoll und e�ektiv einsetzen.

Die folgenden Beispiele zeigen die Syntax von C# exemplarisch. In den Unterabschnitten werden Beson-derheiten der Sprache dargestellt. Der dazugehörige Text und die Beispiele stammen teilweise aus demC#-Programmierhandbuch von Microsoft.

3.1 Syntax

Listing 3.1: C# Grundlagen

//Klasse − Konstruktoren

using System;

/∗ Blockkommentar ∗/abstract class Shape

{

public const double pi = Math.PI;

protected double x, y, z;

public Shape (double x, double y, double z)

{

this.x = x;

this.y = y;

this.z = z;

}

public abstract double Area();

}

class Circle: Shape

{

public Circle(): this(100) //default radius is 100

{

}

public Circle(double radius): base(radius,0,0)

{

}

public override double Area()

{

return pi*x*x;

}

}

class Cylinder: Circle

{

public Cylinder(double radius, double height): base(radius)

{

y = height;

}

public override double Area()

{

return 2*(base.Area()) + 2*pi*x*y;

}

}

class TestClass

17

Page 18: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.1 Syntax

{

public static void Main()

{

double radius = 2.5, height = 3.0;

Circle myCircle = new Circle(radius);

Cylinder myCylinder = new Cylinder(radius, height);

Console.WriteLine("Area of the circle = {0:F2}",

myCircle.Area());

Console.WriteLine("Area of the cylinder = {0:F2}",

myCylinder.Area());

}

}

// versiegelte Klassen = �nal in Java

public sealed class D

{

// Class members here.

}

//statische Klassen

public static class TemperatureConverter

{

public static double CelsiusToFahrenheit(string temperatureCelsius)

{

...

}

}

double F, C = 0;

F = TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine());

//Schnittstellen

public interface IStudentProvider

{

void Add(Student student);

int Count { get; }

}

class DbStudentProvider: IStudentProvider

{

private int _count;

// ...

public DbStudentProvider()

{

_count = 0;

// ...

}

public void Add(Student student)

{

//

}

public int Count

{

get { return _count; }

}

}

//If Statement

if (condition)

{

/∗ code ∗/}

else if (otherCondition)

{

/∗ code ∗/}

else

{

/∗ code ∗/}

//switch − geht auch mit String

int cost = 0;

switch(n)

{

case 1:

cost += 25;

18 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 19: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.1 Syntax

break;

case 2:

cost += 25;

goto case 1;

case 3:

cost += 50;

goto case 1;

default:

Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");

break;

}

//For Schleife

int[] myArray;

for (int i = 0; myArray.Length; i++)

{

Console.WriteLine(myArray[i]);

}

//Foreach Schleife

int[] myArray;

foreach (int element in myArray)

{

Console.WriteLine(element);

}

//Arrays

// Declare a single−dimensional arrayint[] array1 = new int[5];

// Declare and set array element values

int[] array2 = new int[] { 1, 3, 5, 7, 9 };

// Alternative syntax

int[] array3 = { 1, 2, 3, 4, 5, 6 };

// Declare a two dimensional array

int[,] multiDimensionalArray1 = new int[2, 3];

// Declare and set array element values

int[,] multiDimensionalArray2 = { { 1, 2, 3 }, { 4, 5, 6 } };

// Declare a jagged array

int[][] jaggedArray = new int[6][];

// Set the values of the �rst array in the jagged array structure

jaggedArray[0] = new int[4] { 1, 2, 3, 4 };

Frage 3.1Fügen Sie in obigen Code den dazu gleichwertigen Code einer anderen OO-Sprache ein, z.B. Java. MarkierenSie Besonderheiten von C#.

Frage 3.2Werden Interfaces nur in Sprachen benötigt und eingesetzt, die keine Mehrfachvererbung unterstützen?

Frage 3.3Suchen Sie in .Net nach 3 verschiedenen vorde�nierten Interfaces und beschreiben Sie deren Bedeutung.

19 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 20: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.1 Syntax

Ähnlich wie in anderen OO-Sprachen gibt es die Zugri�smodi�zierer private, public und protected. C# unter-stützt auch internal.1

public Auf den Typ oder Member kann von jedem Code in der gleichen Assembly siehe Abschnitt 2.4.1 aufSeite 8 oder einer anderen Assembly, die darauf verweist, zugegri�en werden.

private Auf den Typ oder Member kann nur von Code in der gleichen Klasse oder Struktur zugegri�en werden.

protected Auf den Typ oder Member kann nur von Code in der gleichen Klasse oder Struktur oder in einerabgeleiteten Klasse zugegri�en werden.

internal Auf den Typ oder Member kann von jedem Code in der gleichen Assembly zugegri�en werden, jedochnicht von Code in einer anderen Assembly.

protected internal Auf den Typ oder Member kann von jedem Code in der gleichen Assembly oder von jederabgeleiteten Klasse in einer anderen Assembly zugegri�en werden.

Frage 3.4Beschreiben Sie public, private, internal und protected in eigenen Worten und nennen Sie jeweils einen sinnvollenNutzungsbereich.

Methoden und Konstruktoren können überladen werden, ebenso können die meisten Operatoren überladenwerden: Arithmetische, Vergleichs-, Logik- und Bedingungsoperatoren. Ausnahmen sind Zuweisungsoperatoren,Spezialoperatoren wie sizeof, new, is und typeof.

3.1.1 is vs as

Mit �is� können Sie überprüfen, ob ein Objekt eine Instanz von einer bestimmten Klasse ist. Das Ergebnis istein Boolean-Wert. Mit �as� erhält man direkt das gecastete Objekt bwz. �null�, falls dies nicht möglich ist.

Frage 3.5Wie verhalten sich �as� und �is� bei abgeleiteten Klassen, d.h. was liefert

Listing 3.2: is und as

Circle c = new Circle(3.0);

Shape s;

if (c is Shape)

Console.WriteLine("Circle is Shape.");

s = c as Shape;

Console.WriteLine(s.Area().ToString());

if (s is Circle)

Console.WriteLine("Shape is Circle.");

c = s as Circle;

Console.WriteLine(c.Area().ToString());

1Zugri�smodi�zierer

20 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 21: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

Frage 3.6Wie unterscheiden sich is/as und Casting?

3.1.2 Operatoren

In Bezug auf Operatoren möchte ich auch noch auf den tertiären Operator �?� mit �:� hinweisen, den es auch inanderen Programmiersprachen wie z.B. PHP gibt. Ähnlich dazu, gibt es den �??� Operator. Dieser ist äuÿersthilfreich, wenn Objekte auf null hin überprüft werden sollen. Es wird die Linke Seite evaluiert. Ist diese nichtnull, wird der linke Wert zurückgegeben, andernfalls der rechte.

Listing 3.3: Operatoren

//Variante mit Bedingung?Wahrfall:Falschfall;

string r = ( v == null ) ? "null" : v;

//Variante mit dem ??−Operator:string r = v ?? "null";

//+ einmal anders

class Matrix {

...

Matrix operator +(Matrix l, Matrix r)

{

Matrix e = new Matrix();

e = l mit r verknüpfen

return e;

}

}

...

Matrix a = new Matrix(...), b = new Matrix(...);

Matrix c = a + b;

Frage 3.7Gibt es auÿerhalb von mathematischen Objekten sinnvolle Anwendungsmöglichkeiten für Operatoren? Wennja, wo?

3.1.3 Partial

Die De�nitionen partieller Typen ermöglichen es, die De�nition einer Klasse, einer Struktur oder einer Schnitt-stelle auf mehrere Dateien aufzuteilen. Einen Klassen-, Struktur- oder Schnittstellentyp auf mehrere Dateienaufzuteilen kann hilfreich sein, wenn Sie mit groÿen Projekten oder mit automatisch generiertem Code wiez.B. von Der Windows Forms-Designer bereitgestellt. Verwenden Sie hierfür das Schlüsselwort partial, also z.B.partial class A in zwei Dateien.

Frage 3.8Gibt es das Prinzip von �Partial� auch in Java oder PHP?

3.2 Typen

3.2.1 CTS

Das Common Type System (CTS) ist die Gesamtmenge aller möglichen Eigenschaften und Typen (siehe Abbil-dung 5.3). Die Common Language Speci�cation (CLS) ist eine Untermenge der CTS-Eigenschaften und Typen,

21 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 22: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

Abbildung 3.1: CTS nach ECMA-335

die von jedem Compiler unterstützt werden müssen, um die Interoperabilität zwischen der .Net Applikationenzu garantieren. Nicht CLS-konforme Typen sind beispielsweise Int8 oder Unsigned int64.

Die CLS stellt einen Satz von Merkmalen sicher, die in jeder Sprache, die in die Laufzeitumgebung integriertwird, garantiert zur Verfügung stehen. Die CLS garantiert, dass jedes Programm bzw. jeder Programmteil (z. B.eine einzelne Klasse), der CLS-konform entwickelt worden ist, in jeder anderen CLS-kompatiblen Programmier-sprache vollständig genutzt werden kann2. Beispielsweise gibt es nur einen unicode-basierten Stringtyp und allesist ein Objekt. Dadurch ist die .Net Plattform selbst sprachneutral und jede der dort verwendeten Sprachen istgleich berechtigt.

Betrachtet man das CTS, also die Gesamtzahl aller Typen, so kann man diese für C# wie in Abbildung 3.2gruppieren � vergleiche hierzu auch Abbildung 3.1.

Die Bezeichnung �ValueType� hat nichts mit der Parameterübergabe byref bzw. byval zu tun.

Zwischen den einfachen Typen gibt es die typischen impliziten Konvertierungen. Wir erinnern uns daran, dassalle Typen in C# von object abgeleitet sind. Dies erö�net uns zusätzliche Möglichkeiten: Jeder Datentyp kannals Objekt gespeichert oder übergeben werden.

Der Boxing Vorgang beschreibt die Umwandlung von einem value-type zu einem Objekt. Bei dem BoxingVorgang wird die Instanz eines Objekts erstellt und der ursprüngliche Wert des value-types wird in das neueObjekt kopiert. Man könnte sagen, dies sei der umgekehrte Vorgang. Beim Unboxing Vorgang wird ein value typeaus einem Objekt extrahiert. Der C# Compiler überprüft zusätzlich, ob die von Ihnen angeforderte Variable,welche Sie aus dem Objekt extrahieren wollen, tatsächlich den angeforderten unboxed Type entsprechen kann.

Boxing und Unboxing ist ein zentraler Teil im Type-System von C# (und der Runtime). Es stellt ein Bindegliedzwischen value-types und reference-types zur Verfügung. Value-types haben den Vorteil, dass sie wenig Spei-cherplatz benötigen. In manchen Fällen ist es jedoch von Vorteil, dass value-types auch die die Eigenschaftenvon Objekten haben.

2Die Regeln der CLS gelten dabei immer nur für ö�entliche (public oder protected) Schnittstellen.

22 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 23: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

Abbildung 3.2: Typen im Namespace System

Abbildung 3.3: Boxing und Unboxing nach ECMA 335

Merke 3 (Werttypen)Es gibt Werttypen für Zahlen mit verschiedenen Gröÿen und Genauigkeiten. Einegröÿere Zahl oder eine Zahl mit einer höheren Genauigkeit braucht mehr Speicherauf dem Stack. Jeder Werttyp hat eine Gröÿe. Es ist nicht möglich, einen Wert einesgröÿeren Typs in eine kleinere Variable zu stecken, unabhängig davon, wie groÿ dieZahl tatsächlich ist.

3.2.2 Strukturen

Klassen und Strukturen sind zwei der grundlegenden Konstrukte des allgemeinen Typsystems in .NET Frame-work. Bei beiden handelt es sich um eine Datenstruktur, die einen als logische Einheit zusammengehörenden Satzvon Daten und Verhalten kapselt. Die Daten und Verhalten sind die Member der Klasse oder Struktur. Dieseenthalten deren Methoden, Eigenschaften, Ereignisse usw. Eine Struktur ist ein Werttyp. Wenn eine Strukturerstellt wird, enthält die Variable, der die Struktur zugewiesen wird, die eigentlichen Daten der Struktur. Wenndie Struktur einer neuen Variable zugewiesen wird, werden diese kopiert. Die neue Variable und die ursprüngli-che Variable enthalten daher zwei separate Kopien der gleichen Daten. Änderungen an einer Kopie wirken sichnicht auf die andere Kopie aus.

Im Allgemeinen werden Klassen zum Modellieren von komplexerem Verhalten oder von Daten verwendet, diedafür vorgesehen sind, nach der Erstellung eines Klassenobjekts geändert zu werden. Strukturen sind am bestenfür kleine Datenstrukturen geeignet, die überwiegend Daten enthalten, deren Änderung nach der Erstellung derStruktur nicht vorgesehen ist.

Besonderheiten von Strukturen im Vergleich zu Klassen:

• Strukturen haben fast die gleiche Syntax wie Klassen, unterliegen jedoch mehr Beschränkungen als Klas-sen:

� Innerhalb einer Strukturdeklaration können Felder nur dann initialisiert werden, wenn sie als konstantoder statisch deklariert werden.

23 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 24: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

� Eine Struktur kann keinen Standardkonstruktor (ein Konstruktor ohne Parameter) und keinen De-struktor deklarieren.

� Eine Struktur ist nicht in der Lage, von einer anderen Struktur oder Klasse zu erben, und sie kannauch nicht die Basis einer Klasse sein. Alle Strukturen erben direkt von System.ValueType, welchervon System.Object erbt.

• Strukturen werden durch Zuweisung kopiert. Wenn eine Struktur einer neuen Variablen zugewiesen wird,werden alle Daten kopiert. Änderungen an der neuen Kopie wirken sich nicht auf die Daten in der ur-sprünglichen Kopie aus.

• Strukturen sind Werttypen, und Klassen sind Referenztypen.

• Strukturen können im Gegensatz zu Klassen ohne einen Operator new instanziiert werden.

• Eine Struktur kann Schnittstellen implementieren.

Listing 3.4: Beispiel für Strukturen

public struct Coords

{

public int x, y;

public CoOrds(int p1, int p2)

{

x = p1;

y = p2;

}

}

Merke 4 (struct/Objekt Parameterübergabe)Da Strukturen Werttypen sind und Objekte Referenztypen sind, gilt: Wenn structan eine Methode übergeben wird, wird eine Kopie der Struktur übergeben. Bei derÜbergabe einer class-Instanz wird jedoch ein Verweis übergeben.

3.2.3 Strings

Eine Zeichenfolge ist ein Objekt vom Typ String, dessen Wert Text ist. Intern wird der Text als schreibge-schützte Au�istung von Char-Objekten gespeichert, von denen jedes ein in UTF-16 codiertes Unicode-Zeichendarstellt. Eine C#-Zeichenfolge wird nicht mit einem NULL-Zeichen am Ende terminiert (im Gegensatz zu Cund C++). Deshalb kann eine C#-Zeichenfolge eine beliebige Anzahl eingebetteter NULL-Zeichen ('0') enthalten. Die Länge einer Zeichenfolge stellt die Anzahl der Zeichen unabhängig davon dar, ob die Zei-chen aus Unicode-Ersatzzeichenpaaren gebildet werden oder nicht. Um in einer Zeichenfolge auf die einzelnenUnicode-Codepunkte zuzugreifen, verwenden Sie das StringInfo-Objekt.

In C# ist das string-Schlüsselwort ein Alias für String. Deshalb entsprechen sich String und string, und Sie kön-nen eine beliebige Namenskonvention verwenden. Die String-Klasse stellt viele Methoden zum sicheren Erstellen,Bearbeiten und Vergleichen von Zeichenfolgen bereit.

Initialisieren Sie eine Zeichenfolge mit dem konstanten Empty-Wert, um ein neues String-Objekt zu erstellen,dessen Zeichenfolge die Länge NULL aufweist. Durch die Initialisierung von Zeichenfolgen mit dem Wert Emptyanstelle von null können Sie die Chancen einer NullReferenceException reduzieren. Verwenden Sie die statischeIsNullOrEmpty(String)-Methode, um den Wert einer Zeichenfolge zu prüfen, bevor Sie versuchen, darauf zuzu-greifen.

Da eine Zeichenfolgenänderung einer neuen Zeichenfolgenerstellung gleichkommt, müssen Sie beim Erstellen vonVerweisen auf Zeichenfolgen vorsichtig sein. Wenn Sie einen Verweis auf eine Zeichenfolge erstellen und danndie ursprüngliche Zeichenfolge ändern, zeigt der Verweis weiterhin auf das ursprüngliche Objekt und nicht aufdas neue Objekt, das beim Ändern der Zeichenfolge erstellt wurde. Der folgende Code veranschaulicht diesesVerhalten:

24 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 25: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

Listing 3.5: Stringreferenz

string s1 = "Hello ";

string s2 = s1;

s1 += "World";

System.Console.WriteLine(s2);

//Output: Hello

Listing 3.6: Stringreferenz

string s1 = "Hello ";

string s2 = s1;

s1 = "Hello World";

System.Console.WriteLine(s2);

Frage 3.9Was wird in Listing 3.6 ausgegeben?

Längere Strings oder Pfade können Sie in C# mit einem vorangestellten @ de�nieren.

Listing 3.7: Strings mit @ - ähnlich Heredoc von PHP

string path = @"c:\Docs\Source\a.txt";

string quote = @"Her name was ""Sara.""";

string miniTemplate = @"Hello {0},

Your friend {1} sent you this message:

{2}

That's all!";

string populatedTemplate = String.Format(miniTemplate, "Fred", "Jack", "HelloWorld!");

System.Console.WriteLine(populatedTemplate);

3.2.4 Generika

Manchmal möchte man eine Klasse oder Parameter einer Methode so allgemein halten, dass sie mit Objektenverschiedener Typen arbeiten können. Dies könnte man durch den allgemeinsten Datentyp Object erreichen,damit können Typen jedoch gemischt werden. Möchte man auf Typsicherheit nicht verzichten, so kann manGenerika einsetzen. Häu�g verwendet man generische Listen, um z.B. Stringlisten, Int-Listen oder Listen vonObjekten einer Klasse zu verarbeiten.

Listing 3.8: Generika

public class GenericList<T>

{

void Add(T input) { }

}

class TestGenericList

{

private class ExampleClass { }

static void Main()

{

GenericList<int> list1 = new GenericList<int>();

GenericList<string> list2 = new GenericList<string>();

GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();

}

}

//generische Interfaces

interface IEquatable<T>

{

bool Equals(T obj);

}

public class Car : IEquatable<Car> //weitere implementierte Interfaces durch , abtrennen

25 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 26: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

{

private string make;

private string model;

public string Make

{

get {return make;}

set {make = value;}

}

public string Model

{

get {return model;}

set {model = value;}

}

// Implementation of IEquatable<T> interface

public bool Equals(Car car)

{

if (this.Make == car.Make && this.Model == car.Model)

{

return true;

}

else

return false;

}

}

3.2.5 Listen

Abbildung 3.4: Listen

Abbildung 3.4 stellt wesentliche Listen des Namensraums System.Collection mit den von ihnen implementiertenInterfaces dar. Hashtable stellt eine Au�istung von Schlüssel-Wert-Paaren dar, die nach dem Hashcode desSchlüssels organisiert sind.

Die generische Dictionary<(Of <(TKey, TValue>)>)-Klasse stellt eine Zuordnung von einem Satz von Schlüs-seln zu einem Satz von Werten bereit. Jede Hinzufügung zum Wörterbuch besteht aus einem Wert und demzugeordneten Wenn ein Objekt im Dictionary<(Of <(TKey, TValue>)>) als Schlüssel verwendet wird, darf esnicht auf eine Weise geändert werden, die sich auf seinen Hashwert auswirkt. Jeder Schlüssel in einem Dictio-nary<(Of <(TKey, TValue>)>) muss für den Gleichheitsvergleich des Wörterbuches eindeutig sein.

ArrayList implementiert die IList-Schnittstelle unter Verwendung eines Arrays, das nach Bedarf dynamischvergröÿert wird. Es wird nicht sichergestellt, dass die ArrayList sortiert ist. Sie müssen die ArrayList sortieren,bevor Sie Vorgänge wie BinarySearch durchführen, die eine sortierte ArrayList voraussetzen.

Die List<(Of <(T>)>)-Klasse stellt die generische Entsprechung der ArrayList-Klasse dar. Sie implementiertdie generische IList<(Of <(T>)>)-Schnittstelle unter Verwendung eines Arrays, das nach Bedarf dynamischvergröÿert wird.

Queue stellt eine FIFO-Au�istung (First-In-First-Out) von Objekten dar.

Stack stellt eine LIFO (Last-In-First-Out)-Au�istung variabler Gröÿe von Instanzen desselben beliebigen Typsdar.

Listing 3.9: Listen

Hashtable openWith = new Hashtable();

openWith.Add("txt", "notepad.exe");

26 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 27: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.2 Typen

Dictionary<string, string> openWith2 =

new Dictionary<string, string>();

openWith2.Add("txt", "notepad.exe");

ArrayList myAL = new ArrayList();

myAL.Add("Hello");

List<string> dinosaurs = new List<string>();

dinosaurs.Add("Tyrannosaurus");

Queue<string> numbers = new Queue<string>();

numbers.Enqueue("one");

Stack<string> numbers = new Stack<string>();

numbers.Push("one");

Frage 3.10Diskutieren Sie mögliche Probleme bei folgendem Code:

List<string> ls = new List<string>();

List<object> lo = ls;

lo.add(new Object());

string s = ls.get(0);

3.2.6 Enumerationen

Ein Enumerationstyp (auch als Enumeration bezeichnet) bietet eine e�ziente Möglichkeit, einen Satz benannterintegraler Konstanten zu de�nieren, die einer Variablen zugewiesen werden können. Angenommen, Sie müsseneine Variable de�nieren, deren Wert einen Wochentag darstellt. Es gibt nur sieben sinnvolle Werte, die dieseVariable speichern kann. Um diese Werte zu de�nieren, können Sie einen Enumerationstyp verwenden, der durchdie Verwendung des enum-Schlüsselworts deklariert wird. Der zugrunde liegende Standardtyp aller Elemente inder Enumeration lautet int.

Listing 3.10: Enumerationen

enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

enum Months : byte { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };

Days meetingDay = Days.Monday;

enum MachineState

{

PowerOff = 0,

Running = 5,

Sleeping = 10,

Hibernating = Sleeping + 5

}

Sie können einen Enumerationstyp verwenden, um Bit�ags zu de�nieren. Dadurch kann eine Instanz des Enu-merationstyps eine Kombination aus den Werten speichern, die in der Enumeratorliste de�niert sind.

Im folgenden Beispiel wird eine andere Version des Days-Enumerators, der als Days2 bezeichnet ist, de�niert.Days2 weist das Flags-Attribut auf, und jedem Wert wird die nächste höhere Potenz von 2 zugewiesen. Sokönnen Sie eine Days2-Variable erstellen, deren Wert Days2.Tuesday und Days2.Thursday lautet. Um ein Flageines Enumerators festzulegen, verwenden Sie den logischen OR-Operator. Um zu ermitteln, ob ein bestimmtesFlag festgelegt ist, verwenden Sie eine logische AND-Operation.

27 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 28: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

Listing 3.11: Enumeration als Flags[Flags]

enum Days2

{

None = 0x0,

Sunday = 0x1,

Monday = 0x2,

Tuesday = 0x4,

Wednesday = 0x8,

Thursday = 0x10,

Friday = 0x20,

Saturday = 0x40

}

class MyClass

{

Days2 meetingDays = Days2.Tuesday | Days2.Thursday;

private void pleassure() {

// Initialize with two �ags using bitwise OR.

meetingDays = Days2.Tuesday | Days2.Thursday;

// Set an additional �ag using bitwise OR.

meetingDays = meetingDays | Days2.Friday;

Console.WriteLine("Meeting days are {0}", meetingDays);

// Output: Meeting days are Tuesday, Thursday, Friday

// Remove a �ag using bitwise XOR.

meetingDays = meetingDays ^ Days2.Tuesday;

Console.WriteLine("Meeting days are {0}", meetingDays);

// Output: Meeting days are Thursday, Friday

// Test value of �ags using bitwise AND.

bool test = (meetingDays & Days2.Thursday) == Days2.Thursday;

Console.WriteLine("Thursday {0} a meeting day.", test == true ? "is" : "is not");

// Output: Thursday is a meeting day.

Frage 3.11Welche anderen Programmiersprachen kennen Generika?

Frage 3.12Diskutieren Sie folgende Aussage: �Generische typsichere Listen sind einer �ArrayList� immer vor zu ziehen.�

3.3 Attribute und Re�ektion

Attribute stellen eine e�ziente Methode dar, um Deklarationsinformationen mit C#-Code (Typen, Methoden,Eigenschaften usw.) zu verknüpfen. Sobald das Attribut einer Programmentität zugeordnet ist, kann es zurLaufzeit mithilfe eines Verfahrens abgefragt werden, das als Re�ektion bezeichnet wird.

3.3.1 Attribute

Attribute fügen dem Programm Metadaten hinzu. Metadaten sind Informationen zu den in einem Programmde�nierten Typen. Alle .NET-Assemblys enthalten einen angegebenen Satz von Metadaten, die die in der As-sembly de�nierten Typen und Typmember beschreiben. Sie können benutzerde�nierte Attribute hinzufügen,um bei Bedarf zusätzliche Informationen anzugeben.

Sie können eigene benutzerde�nierte Attribute erstellen, indem Sie eine Attributklasse de�nieren. Dies ist eineKlasse, die direkt oder indirekt Elemente von Attribute ableitet, wodurch die schnelle und einfache Identi�kation

28 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 29: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

von Attributde�nitionen in Metadaten ermöglicht wird. Angenommen, Sie möchten Klassen und Strukturen mitdem Namen des Programmierers kennzeichnen, der sie geschrieben hat. Sie könnten beispielsweise eine benutzer-de�nierte Author-Attributklasse de�nieren: Der Klassenname entspricht dem Attributnamen, also Author. Daer von System.Attribute abgeleitet ist, handelt es sich um eine benutzerde�nierte Attributklasse. Die Parameterdes Konstruktors sind die positionellen Parameter des benutzerde�nierten Attributs (in diesem Fall name), undalle ö�entlichen Felder oder Eigenschaften, die Lese- und Schreibzugri� unterstützen, sind benannte Parameter(in diesem Fall ist version der einzige benannte Parameter). Beachten Sie, dass das AttributeUsage-Attributverwendet wird, um die Gültigkeit des Author-Attributs auf class- und struct-Deklarationen zu beschränken.

3.3.2 Re�ektion

Metadaten eines Moduls können zur Laufzeit ausgelesen und geändert werden Diesen Vorgang nennt manRe�ektion .NET Framework stellt entsprechende Klassen über den Namespace System.Re�ection bereit

3Bei der Re�ektion werden Objekte (vom Typ Type) bereitgestellt, die Assemblys, Module und Typen beschrei-ben. Mithilfe von Re�ektion können Instanzen von Typen dynamisch erzeugt, Typen an ein vorhandenes Objektgebunden und Typinformationen von vorhandenen Objekten abgefragt werden. Ebenso können die Methodenvorhandener Objekte aufgerufen werden, und es kann auf ihre Felder und Eigenschaften zugegri�en werden.Wenn Sie Attribute im Code verwenden, können Sie mithilfe von Re�ektion auf diese Attribute zugreifen.Re�ektion ist in folgenden Situationen nützlich:

• Wenn Sie auf Attribute in den Metadaten des Programms zugreifen müssen.

• Für das Überprüfen und das Instanziieren von Typen in einer Assembly.

• Für das Erstellen neuer Typen zur Laufzeit. Verwenden Sie die Klassen in System.Re�ection.Emit.

• Für das Ausführen von spätem Binden und für den Zugri� auf Methoden von zur Laufzeit erstelltenTypen.

Zugri� auf Attribute mit Re�ektion

Listing 3.12: Benutzerde�niertes Attribute und Re�ektion

//De�nition

[System.AttributeUsage(System.AttributeTargets.Class |

System.AttributeTargets.Struct,

AllowMultiple = true) // multiuse attribute

]

public class Author : System.Attribute

{

string name;

public double version;

public Author(string name)

{

this.name = name;

version = 1.0; // Default value

}

public string GetName()

{

return name;

}

}

[Author("H. Ackerman")]

private class FirstClass

{

// ...

}

// No Author attribute

private class SecondClass

{

// ...

}

3Re�ektion

29 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 30: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

[Author("H. Ackerman"), Author("M. Knott", version = 2.0)]

private class ThirdClass

{

// ...

}

class TestAuthorAttribute

{

static void Main()

{

PrintAuthorInfo(typeof(FirstClass));

PrintAuthorInfo(typeof(SecondClass));

PrintAuthorInfo(typeof(ThirdClass));

}

private static void PrintAuthorInfo(System.Type t)

{

System.Console.WriteLine("Author information for {0}", t);

System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t); // re�ection

foreach (System.Attribute attr in attrs)

{

if (attr is Author)

{

Author a = (Author)attr;

System.Console.WriteLine(" {0}, version {1:f}", a.GetName(), a.version);

}

}

}

}

Frage 3.13Welche Ausgabe erzeugt das Programm?

Frage 3.14Suchen Sie drei Anwendungsfälle, in denen Attribute und Re�ektions eingesetzt werden.

3.3.3 Performance

Zeichenfolgenobjekte sind unveränderlich (immutable). Das bedeutet, dass sie nicht mehr geändert werdenkönnen, nachdem sie erstellt wurden. Alle String-Methoden und C#-Operatoren, mit denen eine Zeichenfolgegeändert werden könnte, geben diese Ergebnisse in einem neuen Zeichenfolgenobjekt zurück. Im folgenden Bei-spiel bleiben beim Verketten des Inhalts von s1 und s2 zu einer einzelnen Zeichenfolge die beiden ursprünglichenZeichenfolgen unverändert. Der Operator += erstellt eine neue Zeichenfolge, die die kombinierten Inhalte ent-hält. Dieses neue Objekt wird der Variablen s1 zugewiesen. Das ursprüngliche Objekt, das s1 zugewiesen wurde,wird für die Garbage Collection freigegeben, da keine andere Variable einen Verweis darauf enthält.

Mit der Klasse StringBuilder können Sie ohne das Zerstören und Neuerstellen von Objekten speichere�zientZeichenketten zusammenführen. Die Klasse enthält auch weitere nützliche Methoden.

Listing 3.13: StringBuilder zur Verkettung von Strings

int x = 4;

StringBuilder sb = new StringBuilder();

sb.append(x.toString());

sb.AppendFormat("GHI{0}{1}", 'J', 'k');

sb.append(" und ");

//Einen Text am Anfang einfügen

sb.Insert(0, "Alphabet: ");

30 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 31: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

sb.Replace('k', 'K');

Console.WriteLine("{0} chars: {1}", sb.Length, sb.ToString());

Noch eine Performanceverbesserung gibt es beim Prüfen auf Leerstrings: Der Vergleich von Zeichenfolgen mitder System.String.Length-Eigenschaft oder der System.String.IsNullOrEmpty(System.String)-Methode ist be-deutend schneller als ein Vergleich mit Equals.

Listing 3.14: String auf leer prüfenstring s1 = "test";

//nicht performant if (s1 == "")

if ( !String.IsNullOrEmpty(s1) )

{

Console.WriteLine("s1 != null and s1.Length != 0.");

}

Neben StringBuilder und dem Prüfen auf Leerstrings gibt es weitere Möglichkeiten, Code performanter zuschreiben. Wenn Sie ein Feld als static und readonly deklarieren und mit einem Wert initialisieren, dann solltenSie stattdessen const verwenden. Der Wert eines const-Felds wird zur Kompilierungszeit berechnet und inden Metadaten gespeichert. Dadurch wird � im Vergleich zu einem static readonly-Feld � die Laufzeitleistunggesteigert. Wenn mit dem is-Operator von C# getestet wird, ob die Umwandlung erfolgreich durchgeführtwerden kann, bevor die eigentliche Umwandlung erfolgt, sollten Sie erwägen, stattdessen das Ergebnis desas-Operators zu testen. Dieses Vorgehen bietet die gleiche Funktionalität ohne die implizite vom is-Operatorausgeführte Umwandlungsoperation.

Listing 3.15: is/as performant einsetzen// The 'is ' statement performs a cast operation.

if(obj is Control)

{

// The 'as' statement performs a duplicate cast operation.

Control aControl = obj as Control;

// Use aControl.

}

//besser:

Control aControl = obj as Control;

if(aControl != null)

{

// Use aControl.

}

Zur Analyse Ihres Codes, können Sie eine hilfreiche Eigentschaft Ihres Projekts setzen: Option Codeanalyse ak-tivieren. Weitere Empfehlungen zur Erstellung von hochwertigem Code �nden Sie unter Verfassen von qualitativhochwertigem Quellcode und darin insbesondere Leistungswarnungen.

3.3.4 IList, IEnumerable und andere Interfaces

Möchte man eine Klasse mit �foreach� durchlaufen, so benötigt man die Methode GetEnumerator.

Listing 3.16: Iteratorenpublic class DaysOfTheWeek : System.Collections.IEnumerable

{

string[] days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

public System.Collections.IEnumerator GetEnumerator()

{

for (int i = 0; i < days.Length; i++)

{

yield return days[i];

}

}

}

class TestDaysOfTheWeek

{

static void Main()

{

// Create an instance of the collection class

31 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 32: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

DaysOfTheWeek week = new DaysOfTheWeek();

// Iterate with foreach

foreach (string day in week)

{

System.Console.Write(day + " ");

}

}

}

Im obigen Listing taucht ein bisher nicht eingeführtes Schlüsselwort auf, �yield�. In der ausführlichen Anleitungfür Iteratoren �ndet sich bei Microsoft hierzu �Durch die yield return-Anweisung wird ein Element in der Quell-sequenz unverzüglich an den Aufrufer zurückgegeben, noch bevor auf das nächste Element in der Quellsequenzzugegri�en wird.�.

�IEnumerator� ist das Basisinterface für alle Collections, es erlaubt das Lesen von Daten, jedoch keinen schreiben-den Zugri�, also keinen Zugri� auf die Referenz der ihm zugrundeliegenden Collection. �IEnumerable� beinhaltetnur die Methode �GetEnumerator�. �IEnumerator� enthält die Methoden �MoveNext�, �Current� und �Reset�.Das Durchlaufen einer foreach-Schleife ist nun mit dem Cursororientierten Lesen in einer Datenbank vergleich-bar: Nach jeder Iteration wird �MoveNext� aufgerufen und �Current� entsprechend gesetzt. Ist das Ende erreicht,wird �Reset� automatisch aufgerufen. Von diesen Interfaces macht auch LINQ (siehe Abschnitt ?? auf Seite ??)intensiv Gebrauch.

Frage 3.15Welchen Wert hat eine Schleifen-Variable am Ende einer foreach-Schleife?

(1) null

(2) letztes Element

(3) erstes Element

(4) keine, der genannten Möglichkeiten

Ist es nicht nur notwendig, über Listenelemente zu iterieren, so bieten sich die Interfaces �ICollection� und �IList�an, bzw. deren generischen Varianten �ICollection<>� und �IList<>�. �ICollection� fügt Methoden wie �Add�und �Remove� und Eigenschaften wie �Count� und �IsReadOnly� hinzu. �IList� erbt von �ICollection� und fügtnoch einen index-basierten Methoden hinzu.

IComparable und IComparer

Dieser Abschnitt stammt aus dem frei zugänglichen online verfügbaren Buch: C# von Eric Gunnerson Die neueSprache für Microsofts .NET-Plattform.

Die Frameworks halten für Klassen oder Strukturen einige sehr gute Methoden zur Sortierung der Klassen-oder Strukturinstanzen bereit. Bei der einfachsten Methode implementiert das Objekt die IComparable-Schnittstelle:

Listing 3.17: IComparable

using System;

public class Employee: IComparable

{

public Employee(string name, int id)

{

this.name = name;

this.id = id;

}

int IComparable.CompareTo(object obj)

{

Employee emp2 = (Employee) obj;

if (this.id > emp2.id)

return(1);

if (this.id < emp2.id)

return(-1);

else

return(0);

}

32 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 33: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

public override string ToString()

{

return(String.Format("{0}:{1}", name, id));

}

string name;

int id;

}

class Test

{

public static void Main()

{

Employee[] arr = new Employee[4];

arr[0] = new Employee("George", 1);

arr[1] = new Employee("Fred", 2);

arr[2] = new Employee("Tom", 4);

arr[3] = new Employee("Bob", 3);

Array.Sort(arr);

foreach (Employee emp in arr)

Console.WriteLine("Employee: {0}", emp);

}

}

Diese Implementierung ermöglicht lediglich eine Sortierung; die Klasse könnte so de�niert werden, dass eine aufMitarbeiter-ID oder Name basierende Sortierung vorgenommen wird, dem Benutzer kann jedoch die Auswahlder Sortierreihenfolge nicht ermöglicht werden.

Die Designer der Frameworks haben die Fähigkeit zur De�nition mehrerer Sortierreihenfolgen bereitgestellt.Jede Sortierreihenfolge wird durch die IComparer-Schnittstelle ausgedrückt, die geeignete Schnittstelle wird derSortier- oder Suchfunktion übergeben.

Die IComparer-Schnittstelle kann für Employee jedoch nicht implementiert werden, da jede Klasse eine Schnitt-stelle nur einmal implementieren kann. Es könnte also nur eine Sortierreihenfolge bereitgestellt werden1 . Es istfür jede Sortierung eine separate Klasse erforderlich, die IComparer implementiert. Die Klasse ist sehr einfach,da nur die Compare()-Funktion implementiert wird:

Listing 3.18: IComparer

using System;

using System.Collections;

class Employee

{

public string name;

}

class SortByNameClass: IComparer

{

public int Compare(object obj1, object obj2)

{

Employee emp1 = (Employee) obj1;

Employee emp2 = (Employee) obj2;

return(String.Compare(emp1.name, emp2.name));

}

}

Das Compare()-Mitglied trägt zwei Objekte als Parameter. Da die Klasse nur zur Mitarbeitersortierung einge-setzt werden sollte, werden die object-Parameter per Typumwandlung zu Employee. Anschlieÿend wird die instring enthaltene Compare()-Funktion für den Vergleich verwendet.

Die Employee-Klasse wird demnach folgendermaÿen überarbeitet. Die Klassen für die Sortierungen werdeninnerhalb der Employee-Klasse verschachtelt:

Listing 3.19: IComparable

using System;

using System.Collections;

public class Employee: IComparable

{

public Employee(string name, int id)

33 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 34: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

{

this.name = name;

this.id = id;

}

int IComparable.CompareTo(object obj)

{

Employee emp2 = (Employee) obj;

if (this.id > emp2.id)

return(1);

if (this.id < emp2.id)

return(-1);

else

return(0);

}

public override string ToString()

{

return(name + ":" + id);

}

public class SortByNameClass: IComparer

{

public int Compare(object obj1, object obj2)

{

Employee emp1 = (Employee) obj1;

Employee emp2 = (Employee) obj2;

return(String.Compare(emp1.name, emp2.name));

}

}

public class SortByIdClass: IComparer

{

public int Compare(object obj1, object obj2)

{

Employee emp1 = (Employee) obj1;

Employee emp2 = (Employee) obj2;

return(((IComparable) emp1).CompareTo(obj2));

}

}

string name;

int id;

}

class Test

{

public static void Main()

{

Employee[] arr = new Employee[4];

arr[0] = new Employee("George", 1);

arr[1] = new Employee("Fred", 2);

arr[2] = new Employee("Tom", 4);

arr[3] = new Employee("Bob", 3);

Array.Sort(arr, (IComparer) new Employee.SortByNameClass());

// Mitarbeiter sind jetzt nach Name sortiert

foreach (Employee emp in arr)

Console.WriteLine("Employee: {0}", emp);

Array.Sort(arr, (IComparer) new Employee.SortByIdClass());

// Mitarbeiter sind jetzt nach ID sortiert

foreach (Employee emp in arr)

Console.WriteLine("Employee: {0}", emp);

ArrayList arrList = new ArrayList();

arrList.Add(arr[0]);

arrList.Add(arr[1]);

arrList.Add(arr[2]);

arrList.Add(arr[3]);

arrList.Sort((IComparer) new Employee.SortByNameClass());

foreach (Employee emp in arrList)

34 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 35: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

Console.WriteLine("Employee: {0}", emp);

arrList.Sort(); // Standard = nach ID

foreach (Employee emp in arrList)

Console.WriteLine("Employee: {0}", emp);

}

}

Frage 3.16Warum wirft folgender Code eine Ausnahme?

class Employee

{

public string Name { get; set; }

public int Salary { get; set; }

}

...

List<Employee> empList = new List<Employee>() { new Employee { Name = "a", Salary = 14000 }, new Employee { Name

= "b", Salary = 13000 } };

empList.Sort();

Und wie sieht es hiermit aus?

IList<string> strings = new List<string>();

IList<object> objects = strings;

objects[0] = 42;

string s = strings[0];

IDisposable

IDisposable de�niert eine Methode zur Freigabe von reservierten Ressourcen. Über diese Schnittstelle werdenhauptsächlich nicht verwaltete Ressourcen freigegeben. Der Garbage Collector gibt automatisch den für einverwaltetes Objekt reservierten Speicher frei, wenn dieses Objekt nicht mehr verwendet wird. Es kann allerdingsnicht vorausgesagt werden, wann die Garbage Collection statt�ndet. Darüber hinaus hat der Garbage Collectorkeine Kenntnis von nicht verwalteten Ressourcen wie Fensterhandles oder o�enen Dateien und Streams.

Mit der Dispose-Methode dieser Schnittstelle können nicht verwaltete Ressourcen in Verbindung mit dem Gar-bage Collector explizit freigegeben werden. Der Consumer eines Objekts kann diese Methode aufrufen, wenn dasObjekt nicht mehr benötigt wird. Verwenden Sie für den Aufruf einer Klasse, die die IDisposable-Schnittstelleimplementiert, einen try-�nally-Block, um sicherzustellen, dass nicht verwaltete Ressourcen auch dann freige-geben werden, wenn die Anwendung aufgrund einer Ausnahme beendet wird.

Das Muster zum Verwerfen eines Objekts, Dispose-Muster genannt, legt eine Ordnung für die Lebensdauer vonObjekten fest.

Die Dispose-Methode eines Typs sollte alle Ressourcen freigeben, die dieser besitzt. Sie sollte auÿerdem alle Res-sourcen freigeben, deren Eigentümer die Basistypen der Methode sind, indem die Dispose-Methode des überge-ordneten Typs aufgerufen wird. Die Dispose-Methode des übergeordneten Typs sollte alle Ressourcen freigeben,die dieser besitzt, und dann die Dispose-Methode seines übergeordneten Typs aufrufen. Dieses Muster wirddurch die Hierarchie der Basistypen weitergegeben. Um sicherzustellen, dass Ressourcen immer entsprechendbereinigt werden, sollte eine Dispose-Methode auch mehrmals aufgerufen werden können, ohne eine Ausnahmeauszulösen.

Durch das Implementieren der Dispose-Methode für Typen, die nur verwaltete Ressourcen (z. B. Arrays) ver-wenden, werden keine Leistungsvorteile erzielt, da diese automatisch vom Garbage Collector freigegeben werden.

35 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 36: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.3 Attribute und Re�ektion

Verwenden Sie die Dispose-Methode primär für verwaltete Objekte, die systemeigene Ressourcen verwenden, undfür COM-Objekte, die für .NET Framework verfügbar sind. Verwaltete Objekte, die systemeigene Ressourcen(z. B. die FileStream-Klasse) verwenden, implementieren die IDisposable-Schnittstelle.

Eine Dispose-Methode sollte die SuppressFinalize-Methode für das Objekt aufrufen, das es freigibt. Wenn sichdas Objekt gegenwärtig in der Finalisierungswarteschlange be�ndet, verhindert SuppressFinalize, dass dessenFinalize-Methode aufgerufen wird. Beachten Sie, dass das Ausführen einer Finalize-Methode hohen Leistungs-aufwand erfordert. Wenn Sie das Objekt bereits mit der Dispose-Methode bereinigt haben, muss die Finalize-Methode des Objekts nicht mehr vom Garbage Collector aufgerufen werden.

Im folgenden Codebeispiel wird das empfohlene Entwurfsmuster für das Implementieren einer Dispose-Methodefür Klassen veranschaulicht, die nicht verwaltete Ressourcen kapseln.

Ressourcenklassen werden i. d.R. von komplexen systemeigenen Klassen oder APIs abgeleitet und müssen ent-sprechend angepasst werden. Verwenden Sie dieses Codemuster als Anfangspunkt zum Erstellen einer Ressour-cenklasse, und stellen Sie die erforderliche Anpassung basierend auf den Ressourcen, die Sie kapseln, bereit.

Listing 3.20: Dispose-Pattern

//Klasse, die IDisposable implementiert.

public class DisposableResource : IDisposable

{

private Stream resource;

private bool disposed;

// The stream passed to the constructor

// must be readable and not null.

public DisposableResource(Stream stream)

{

if (stream == null)

throw new ArgumentNullException("Stream in null.");

if (!stream.CanRead)

throw new ArgumentException("Stream must be readable.");

resource = stream;

disposed = false;

}

// Demonstrates using the resource.

// It must not be already disposed.

public void DoSomethingWithResource() {

if (disposed)

throw new ObjectDisposedException("Resource was disposed.");

// Show the number of bytes.

int numBytes = (int) resource.Length;

Console.WriteLine("Number of bytes: {0}", numBytes.ToString());

}

public void Dispose()

{

Dispose(true);

// Use SupressFinalize in case a subclass

// of this type implements a �nalizer .

GC.SuppressFinalize(this);

}

protected virtual void Dispose(bool disposing)

{

// If you need thread safety, use a lock around these

// operations, as well as in your methods that use the resource.

if (!disposed)

{

if (disposing) {

if (resource != null)

resource.Dispose();

Console.WriteLine("Object disposed.");

}

// Indicate that the instance has been disposed.

36 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 37: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.4 Sprachbesonderheiten

resource = null;

disposed = true;

}

}

}

//Aufrufende Klasse

class Program

{

static void Main()

{

try

{

// Initialize a Stream resource to pass

// to the DisposableResource class.

Console.Write("Enter filename and its path: ");

string fileSpec = Console.ReadLine();

FileStream fs = File.OpenRead(fileSpec);

DisposableResource TestObj = new DisposableResource(fs);

// Use the resource.

TestObj.DoSomethingWithResource();

// Dispose the resource.

TestObj.Dispose();

}

catch (FileNotFoundException e)

{

Console.WriteLine(e.Message);

}

}

}

Frage 3.17Betrachten Sie den Pizzaservice aus EWA. In der Methode �GenerateView� möchten Sie HTML-Code auf Basisder in einer Datenbank hinterlegten Speisekarte ausgeben. Skizzieren Sie das Gerüst dieser Methode in C# undgeben Sie an, welche Datentypen Sie verwenden würden.

3.4 Sprachbesonderheiten

using Mit dem using-Statement stellen Sie einen korrekten Zugri� auf IDisposable-Objekte sicher. Disposewird automatisch beim Auftreten einer Ausnahme aufgerufen.

Listing 3.21: using

//Using Statement

using (StreamWriter sw = new StreamWriter(@"C:\MyFile.txt"))

{

sw.Write("Hello, File");

37 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 38: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.4 Sprachbesonderheiten

}

3.4.1 Log/Trace

Mit der Trace-Klasse lassen sich Anwendungen instrumentieren. Von einer laufenden Anwendung können Siedamit Informationsmeldungen erhalten, die beim Diagnostizieren von Problemen oder Analysieren der Leistunghilfreich sind.

Listing 3.22: Trace

Trace.WriteLine("Entering Main");

Mächtiger und besser zu kon�gurieren ist log4net.

3.4.2 Ausnahmen

4Die Features zur Ausnahmebehandlung in C# helfen Ihnen, wenn bei der Ausführung eines Programms uner-wartete oder auÿergewöhnliche Situationen auftreten. Bei der Ausnahmebehandlung wird mithilfe der Schlüs-selwörter try, catch und �nally versucht, Aktionen auszuführen, die möglicherweise fehlschlagen, um Fehlerzu behandeln und anschlieÿend die Ressourcen zu bereinigen. Ausnahmen können von der Common LanguageRuntime (CLR � ähnlich zur Virtual Maschine von Java), durch .NET Framework oder Bibliotheken von Drit-tanbietern oder durch den Anwendungscode generiert werden. Ausnahmen werden mit dem throw-Schlüsselworterstellt. Es gibt jedoch im Gegensatz zu Java keine Syntax, die kennzeichnet, welche Klassen welche Ausnahmenwerfen können � throw wird in C# also nur zum tatsächlichen Erzeugen/werfen von Ausnahmen verwendet.

In vielen Fällen wird eine Ausnahme nicht von einer Methode ausgelöst, die direkt durch den Code aufgerufenwurde, sondern von einer anderen Methode, die sich weiter unten in der Aufru�iste be�ndet. In diesem Fallentlädt die CLR die Aufru�iste, sucht eine Methode mit einem catch-Block für den spezi�schen Ausnahmetypund führt den ersten catch-Block aus, der gefunden wird. Falls kein entsprechender catch-Block in der Aufru�istegefunden wird, wird der Prozess beendet und eine Meldung für den Benutzer angezeigt.

Ausnahmen sind Typen, die letztlich alle von System.Exception abgeleitet werden. Fangen Sie keine Ausnahmeab, es sei denn, Sie können sie bearbeiten, und belassen Sie die Anwendung in einem bekannten Zustand. WennSie System.Exception abfangen, lösen Sie es erneut mit dem throw-Schlüsselwort am Ende des catch-Blocks aus.Ausnahmeobjekte enthalten ausführliche Informationen zum Fehler, z. B. den Zustand der Aufru�iste und eineTextbeschreibung des Fehlers. Nicht abgefangene Ausnahmen werden von einem generischen Ausnahmehandlerdes Systems behandelt, der ein Dialogfeld anzeigt.

Im Gegensatz zu Java, gibt es keine Checked Exceptions. Eine Checked Exception ist eine Ausnahme, bei der derCompiler prüft, ob alle Stellen, wo sie auftreten kann, durch Code zum Abfangen der Ausnahme abgedeckt sind.Der Code zum Abfangen kann dabei innerhalb derselben Methode stehen, in der die Ausnahme auftreten kann,oder auch in aufrufenden Methoden. Der Compiler wird bei Java durch das Schlüsselwort throws unterstüzt. BeiC# ist dies nicht so umgesetzt. Fängt niemand eine Exception, so bricht das Programm mit dem generischenFehlerdialog ab. Per Design ist dies vermutlich ein Nachteil. Da jedoch viele Java-Programmierer einfach mit

Listing 3.23: Schlechter Code � Fehler schlucken

catch (E e) {}

Fehler schlucken, führt dies weningstens nicht zu nicht-erklärbaren Verhalten, sondern der Fehler bleibt �erhal-ten�.

Frage 3.18Was passiert bei folgendem Code? Was passiert, wenn der Pfad nicht existiert?

4Ausnahmen und Ausnahmebehandlung

38 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 39: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.4 Sprachbesonderheiten

Listing 3.24: using und Ausnahmen

using (StreamWriter sw = new StreamWriter(@"C:\tmp\MyFile.txt"))

{

sw.Write("Hello, File");

throw new Exception("hey");

sw.Write("Hello, File 2");

}

Console.WriteLine("done");

3.4.3 Indexer

Indexer ermöglichen das Indizieren von Instanzen einer Klasse oder Struktur auf dieselbe Weise wie Arrays.Abgesehen davon, dass ihre Accessoren Parameter akzeptieren, sind Indexer mit Eigenschaften vergleichbar.

Im folgenden Beispiel wird eine generische Klasse de�niert, die mit einfachen get-Accessormethoden und set-Accessormethoden ausgestattet ist. Mit diesen Methoden werdenWerte zugewiesen und abgerufen. Die Program-Klasse erstellt eine Instanz dieser Klasse für das Speichern von Zeichenfolgen.

Listing 3.25: Indexer

class SampleCollection<T>

{

private T[] arr = new T[100];

public T this[int i]

{

get

{

return arr[i];

}

set

{

arr[i] = value;

}

}

}

// This class shows how client code uses the indexer

class Program

{

static void Main(string[] args)

{

SampleCollection<string> stringCollection = new SampleCollection<string>();

stringCollection[0] = "Hello, World";

System.Console.WriteLine(stringCollection[0]);

}

}

Übersicht über Indexer

• Durch Indexer können Objekte auf ähnliche Weise wie Arrays indiziert werden.

• Ein get-Accessor gibt einen Wert zurück. Ein set-Accessor weist einen Wert zu.

• Das this-Schlüsselwort wird zum De�nieren der Indexer verwendet.

• Mithilfe des value-Schlüsselworts wird der Wert de�niert, der vom set-Indexer zugewiesen wird.

• Indexer müssen nicht mit ganzzahligen Werten indiziert werden. Sie können frei wählen, wie Sie denbestimmten Suchmechanismus de�nieren.

• Indexer können überladen werden.

• Indexer können mehr als einen formalen Parameter haben, z. B. wenn auf ein zweidimensionales Arrayzugegri�en wird.

39 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 40: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.5 Serialisierung

Frage 3.19Gibt es das Konzept �Indexer� auch in anderen Programmiersprachen? Wie ist es dort umgesetzt?

3.5 Serialisierung

In .Net gibt es drei Möglichkeiten zur Serialisierung, also zur Persistierung von Objektzuständen. Wir betrachtenalle drei Möglichkeiten anhand folgendem Beispiel, das sowohl ö�entliche als auch private Zustandsvariablen undListen von Objekten enthält. Damit ein Objekt serialisiert werden kann, muss die Klasse mit einem Attribut als�serialisierbar� gekennzeichnet werden. Dies muss für jede verwendete Klasse separat geschehen � im gegebenenBeispiel also für die Klasse �Example� und die Klasse �Student�. Fehlt dieses Attribut, so wird ein Laufzeitfehlergeworfen. Die folgenden Listings zeigen die Serialisierungsmöglichkeiten im Überblick.

Listing 3.26: Serialisierung, Vorbereitung

�using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Collections;

namespace CCompact

{

[Serializable()]

public class Example

{

private int _privateInt;

public int PublicInt;

private List<Student> _privateStudentList;

public List<Student> PublicStudentList;

public Example(int i, List<String> names)

{

_privateInt = i;

PublicInt = i + 1;

_privateStudentList = new List<Student>();

PublicStudentList = new List<Student>();

int j = 0;

foreach (string name in names)

{

_privateStudentList.Add(new Student(name, j++.ToString()));

PublicStudentList.Add(new Student("P" + name, j++.ToString()));

}

}

public override string ToString()

{

StringBuilder sb = new StringBuilder();

sb.Append("\nprivate int: ");

sb.Append(_privateInt.ToString());

sb.Append("\npublic int: ");

sb.Append(PublicInt);

sb.Append("\nprivate student list:\n");

foreach (Student student in _privateStudentList)

{

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("\npublic student list:\n");

foreach(Student student in PublicStudentList)

{

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("objekt end\n--------------\n");

return sb.ToString();

}

}

[Serializable()]

public class Student

{

private string _name;

private string _mark;

public Student(string name, string mark)

{

40 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 41: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.5 Serialisierung

_name = name;

_mark = mark;

}

public override string ToString()

{

return _name + ":" + _mark;

}

}

[Serializable()]

public class ExampleSoap

{

private int _privateInt;

public int PublicInt;

private ArrayList _privateStudentList;

public ArrayList PublicStudentList;

public ExampleSoap(int i, List<String> names)

{

_privateInt = i;

PublicInt = i + 1;

_privateStudentList = new ArrayList();

PublicStudentList = new ArrayList();

int j = 0;

foreach (string name in names)

{

_privateStudentList.Add(new Student(name, j++.ToString()));

PublicStudentList.Add(new Student("P" + name, j++.ToString()));

}

}

public override string ToString()

{

StringBuilder sb = new StringBuilder();

sb.Append("\nprivate int: ");

sb.Append(_privateInt.ToString());

sb.Append("\npublic int: ");

sb.Append(PublicInt);

sb.Append("\nprivate student list:\n");

Student student;

foreach (object obj in _privateStudentList)

{

student = obj as Student;

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("\npublic student list:\n");

foreach (object obj in PublicStudentList)

{

student = obj as Student;

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("objekt end\n--------------\n");

return sb.ToString();

}

}

public class ExampleXml

{

private int _privateInt;

public int PublicInt;

private List<StudentXml> _privateStudentList;

public List<StudentXml> PublicStudentList;

public ExampleXml()

{

_privateStudentList = new List<StudentXml>();

PublicStudentList = new List<StudentXml>();

}

public ExampleXml(int i, List<String> names)

{

_privateInt = i;

PublicInt = i + 1;

_privateStudentList = new List<StudentXml>();

PublicStudentList = new List<StudentXml>();

int j = 0;

foreach (string name in names)

{

_privateStudentList.Add(new StudentXml(name, j++.ToString()));

41 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 42: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.5 Serialisierung

PublicStudentList.Add(new StudentXml("P" + name, j++.ToString()));

}

}

public override string ToString()

{

StringBuilder sb = new StringBuilder();

sb.Append("\nprivate int: ");

sb.Append(_privateInt.ToString());

sb.Append("\npublic int: ");

sb.Append(PublicInt);

sb.Append("\nprivate student list:\n");

foreach (StudentXml student in _privateStudentList)

{

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("\npublic student list:\n");

foreach (StudentXml student in PublicStudentList)

{

sb.Append(student.ToString());

sb.Append("\n");

}

sb.Append("objekt end\n--------------\n");

return sb.ToString();

}

}

public class StudentXml

{

private string _name;

public string Name

{

get { return _name; }

set { _name = value; }

}

private string _mark;

public string Mark

{

get { return _mark; }

set { _mark = value; }

}

public StudentXml()

{

}

public StudentXml(string name, string mark)

{

_name = name;

_mark = mark;

}

public override string ToString()

{

return _name + ":" + _mark;

}

}

}

Listing 3.27: Serialisierung�using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

using System.Runtime.Serialization.Formatters.Binary;

using System.Runtime.Serialization.Formatters.Soap;

using System.Xml.Serialization;

using System.Xml;

namespace CCompact

{

public class SerializeExample

{

const string FILENAME = "exampleData.dat";

public static void Run()

42 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 43: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.5 Serialisierung

{

Console.Write("Using BinaryFormatter");

List<string> names = new List<string>();

names.Add("Asterix");

names.Add("Obelix");

Example exObj = new Example(42, names);

Console.WriteLine("Aktuelles Objekt vor der Serialisierung:");

Console.Write(exObj.ToString());

FileStream writeStream = new FileStream(FILENAME, FileMode.Create);

SerializeBinary(exObj, writeStream);

writeStream.Close();

exObj = null;

Console.WriteLine("set exObj = null!");

FileStream readStream = new FileStream(FILENAME, FileMode.Open);

exObj = DeserializeBinary(readStream);

readStream.Close();

Console.Write("Objekt nach der Serialisierung und Deserialisierung:");

Console.Write(exObj.ToString());

Console.Write("\n\nUsing SOAPFormatter");

ExampleSoap exObjSoap = new ExampleSoap(42, names);

Console.WriteLine("Aktuelles Objekt vor der Serialisierung:");

Console.Write(exObjSoap.ToString());

writeStream = new FileStream(FILENAME, FileMode.Create);

SerializeSOAP(exObjSoap, writeStream);

writeStream.Close();

exObjSoap = null;

Console.WriteLine("set exObjSoap = null!");

readStream = new FileStream(FILENAME, FileMode.Open);

exObjSoap = DeserializeSOAP(readStream);

readStream.Close();

Console.Write("Objekt nach der Serialisierung und anschliessenden Deserialisierung:");

Console.Write(exObj.ToString());

Console.Write("\n\nUsing XmlSerializer");

ExampleXml exObjXml = new ExampleXml(42, names);

Console.WriteLine("Aktuelles Objekt vor der Serialisierung:");

Console.Write(exObjXml.ToString());

XmlTextWriter writer = new XmlTextWriter(FILENAME, Encoding.UTF8);

SerializeXML(exObjXml, writer);

writer.Close();

exObjXml = null;

Console.WriteLine("set exObjXml = null!");

XmlTextReader reader = new XmlTextReader(FILENAME);

exObjXml = DeserializeXML(reader);

reader.Close();

Console.Write("Objekt nach der Serialisierung und anschliessenden Deserialisierung:");

Console.Write(exObjXml.ToString());

Console.ReadLine();

}

private static void SerializeBinary(Example exObj, FileStream stream)

{

BinaryFormatter serializer = new BinaryFormatter();

serializer.Serialize(stream, exObj);

}

private static Example DeserializeBinary(FileStream stream)

{

BinaryFormatter serializer = new BinaryFormatter();

Example exObj = serializer.Deserialize(stream) as Example;

return exObj;

}

private static void SerializeXML(ExampleXml exObj, XmlTextWriter writer)

{

XmlSerializer serializer = new XmlSerializer(typeof(ExampleXml));

serializer.Serialize(writer, exObj);

}

private static ExampleXml DeserializeXML(XmlTextReader reader)

{

XmlSerializer serializer = new XmlSerializer(typeof(ExampleXml));

ExampleXml exObj = serializer.Deserialize(reader) as ExampleXml;

return exObj;

}

private static void SerializeSOAP(ExampleSoap exObj, FileStream stream)

{

43 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 44: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.5 Serialisierung

SoapFormatter serializer = new SoapFormatter();

serializer.Serialize(stream, exObj);

stream.Close();

}

private static ExampleSoap DeserializeSOAP(FileStream stream)

{

SoapFormatter serializer = new SoapFormatter();

ExampleSoap exObj = serializer.Deserialize(stream) as ExampleSoap;

return exObj;

}

}

}

BinaryFormatter Der BinaryFormatter benötigt einen Stream und das zu serialisierende Objekt.

SoapFormatter Für diese Serialisierung müssen Sie zunächst die Referenz auf �Sys-tem.Runtime.Serialization.Formatters.Soap.dll� hinzufügen. Leider unterstützt der SOAP-Formatter keinegenerischen Typen, die Studentenliste kann so also nicht serialisiert werden. Ersetzt man jedoch den generischenTyp durch beispielsweise eine �ArrayList�, dann funktioniert es wieder problemlos. Die erzeugte Datei enthältkorrektes SOAP.

Durch Implementation der Schnittstelle �ISerializable� lässt sich das erzeugte SOAP auch weiter kon�gurieren.

Merke 5 (Serialisierung mit SOAP)Der SoapFormatter kann generische Typen nicht serialisieren.

XMLSerializer XML-Serialisierung ist gut für den Austausch von Daten mit anderen Applikationen. Leidersind an die XML-Serialisierung weitere Nebenbedingungen geknüpft. Zunächst wird ein leerer Konstruktorbenötigt � dies gilt natürlich auch für die verwendete Student-Klasse. Das Attribut �Serializable� wird dagegennicht mehr benötigt. Initialisiert der leere Konstruktor auch den privaten Member �� mit einer leeren Liste,dann funktionieren Serialisierung und Deserialisierung:

Ein Blick auf die deserialisierten Daten und das XML o�enbart jedoch: Es werden standardmäÿig nur dieö�entlichen Werttypen serialisiert. Der private int-Wert ist also nicht persistent abgelegt, ebenso wenig wiedie Studentenobjekte. Wir refactorn die Student-Klasse und extrahieren die Felder zu den privaten Membern.In der Beispielklasse verzichten wir nun auf die privaten Member, da sie nur Demonstrationszwecken dienten.Nun funktioniert die Serialisierung und Deserialisierung. Mit entsprechenden Attributen kann man das erzeugteXML auch de�nieren, siehe Steuern der XML-Serialisierung mit Attributen.

Anmerkung: Während der SOAP-Formatter generische Listen nicht serialisieren kann und die Alternative Ar-rayList funktionierte, kann der XML-Serialisierer mit ArrayList nichts anfangen.

Merke 6 (Serialisierung mit XML)Der XmlSerializer benötigt einen leeren Konstruktor und kann keine privaten Memberserialisieren.

44 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 45: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.6 P2 - C# kompakt

3.6 P2 - C# kompakt

Abgabetermin: 10.04.2011

Merke 7 (Option Code Analysis)Aktivieren Sie von nun an für alle Projekte und abzugebenen Praktikumsaufgen �Ena-ble Code Analysis on Build� mit �Microsoft all rules�, XML-Dokumentation und setzenSie sich mit den Coding Guidelines auseinander.

Abbildung 3.5: MP3-Player, schlechter Entwurf

Aufgabe P2.1Diskutieren Sie den in Abbildung 3.5 abgebildeten Entwurf in Bezug auf:

(a) Funktionalität: Lassen sich mit diesem Entwurf die gewünschten Anforderungen umsetzen? Wenn nein,was fehlt? Vervollständigen Sie den Entwurf ggf.

(b) Namenskonvention: Sind die Klassen, Interfaces und Methoden einheitlich, z.B. nach den Microsoft Gui-delines, benannt? Korrigieren Sie ggf.

(c) OO-Prinzipien: Benennen Sie drei wichtige OO-Prinzipien (wie z.B. �Komposition vor Vererbung�) unduntersuchen Sie den Entwurf daraufhin. Welche Verbesserungen schlagen Sie vor?

(d) Erweiterbarkeit: In Zukunft soll es auch möglich sein, Videos abzuspielen. Es wird auch über eine Web-Anzeige oder eine Windows7-App nachgedacht. Ist dies mit diesem Entwurf möglich? Nennen Sie weiteremögliche Erweiterungen und diskutieren Sie die Möglichkeiten anhand des gegebenen Entwurfs.

(e) Automatische Tests: Welche Teile lassen sich automatisch testen? Entwickeln Sie geeignete Testfälle.

Aufgabe P2.2Implementieren Sie das Grundgerüst Ihres Audio-Players inklusive automatischer Tests. Auf die Methoden Play,Stop etc. müssen Sie noch nicht eingehen.

45 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 46: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

(a) Implementieren Sie Methoden zum Hinzufügen und Entfernen von Playlists.

(b) Implementieren Sie Methoden zum Hinzufügen und Entfernen von Elementen einer Playlist.

(c) Implementieren Sie Methoden zum Serialisieren und Deserialisieren einer Playlist.

Aufgabe P2.3Implementieren Sie eine Basisklasse, die ein einfaches Interface implementiert. Erstellen Sie weitere zwei abge-leitete Klassen. Die Klassen sollen private Member, Methoden und ö�entliche Properties und Methoden sowieeinen Indexer enthalten. Verwenden Sie optionale Parameter und Methodenüberladung. Verwenden Sie generi-sche Listen.

(a) Serialisieren Sie auf drei verschiedene Arten.

(b) Deserialisieren Sie auf drei verschiedene Arten.

(c) Vewenden Sie beim XMLSerializer geeignete Attribute. Lesen Sie diese an anderer Stelle aus und gebenSie sie auf der Console aus.

Aufgabe P2.4Erläutern Sie den Unterschied von Exceptions in .Net im Vergleich zu PHP oder Java.

Aufgabe P2.5Implementieren Sie einen Struct �Bruch� zum Rechnen mit Brüchen. Implementieren Sie mittels Operator-Overloading die Addition, Multiplikation und den Vergleich von Brüchen.

3.7 Delegates

Ein Delegate ist ein Objekt, welches auf eine Methode zeigt, die zu einem späteren Zeitpunkt aufgerufen werdenkann.

Für C++ Programmierer sind Delegate typsichere Funktionszeiger. Für andere einfach ein Datentyp, der Me-thoden aufnehmen kann. Dies ist für Python- Entwicklerinnen oder Anhängern der funktionale Programmierungkeine Besonderheit. Für .Net sind Delegates die Basis des Eventhandlings und für asynchrone Programme. Wiewird ein Delegat de�niert? Betrachten wir folgendes Beispiel:

1 delegate double MathDelegate(double angle);

2 MathDelegate calculator;

3 calculator = Math.Sin //oder auch = new MathDelegate(Math.Sin)

4 System.Diagnostics.Debug.WriteLine(calculator(45))

Zeile 1 de�niert einen bestimmten Delegattyp (MathDelegate), Zeile 2 deklariert eine Delegatvariable (calcula-tor) und Zeile 3 instanziiert sie mit einer statischen Methode. Die Delegatvariable wird nun wie die Methodedie sie enthält verwendet (Zeile 4).

Was passiert dabei im Hintergrund? In .Net sind alles Objekte, von welcher Klasse ist also �calculator� eineInstanz? Durch das Schlüsselwort delegate in Zeile 1 wird eine neue Klasse erzeugt, die von der Basisklasse�System.MulticastDelegate� abgeleitet wird. Zeile 1 de�niert dabei auch genau, welche Art von Funktionen�aufgenommen� werden können, nämlich nur die Methoden, die der angegebenen Signatur folgen, also ein doubleals Argument erwarten und ein double zurückgeben. Auf Multicast-Delegates, die wir also eigentlich immererhalten, werden wir in Abschnitt 3.7.2 auf Seite 49 genauer eingehen. Wichtig ist hierbei: Da wir von dieserKlasse erben, erben wir auch alle Methoden, Eigenschaften und Operatoren. Es lohnt sich also, die Referenz zukonsultieren: MulticastDelegate Class.

Im obigen Beispiel wurde eine statische Methode, also eine zustandslose Methode, verwendet. Delegates könnenaber auch Objektmethoden aufnehmen:

public class Discounter

{

private string name;

public Discounter(string name)

{

this.name = name;

}

public string GetGreeting()

{

return "Hi " + this.name;

}

46 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 47: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

}

public class Shop

{

delegate string GreetingDelegate();

public void Test()

{

Discounter disc = new Discounter("donald");

greet = disc.GetGreeting

System.Diagnostics.Debug.WriteLine(greet())

}

}

Generell ist zu beachten, dass private Methoden auch für Delegate privat bleiben, d.h. auch ein Delegat kannnicht auf eine private Methode einer anderen Klasse zugreifen. Abstrakte Methoden können nicht aufgenommenwerden, es muss immer eine konkrete Implementation existieren. Virtual deklarierte Methoden, sind dagegenkein Problem.

Bezüglich der de�nierten Signatur eines Delegattypen ist wichtig, dass die aufzunehmenden Methoden nichtwirklich identische Signaturen aufweisen müssen, sondern abgeleitete Klassen bzgl. des Rückgabewertes undBasisklassen bzgl. der Argumente auch zulässig sind:

public delegate Employee HandlerMethod(Manager man);

HandlerMethod worker;

worker = FirstHandler;

public static Manager FirstHandler(Employee worker)

{

}

public class Manager : Employee

{

}

Die Signatur der aufzunehmenden Methode darf abweichen:

• Der Rückgabewert muss vom vorgegebenen oder einem davon abgeleiteten Objekttyp sein, d.h. das Delegatde�niert Employee als geforderten Rückgabewert und akzeptiert alle Methoden, die Employee oder vonEmployee abgeleitete Klassen zurückgeben. Dies läuft unter dem Begri� Covariance.

• Die Argumente müssen vom vorgegebenen oder einem dazugehörigen Basistyp sein, d.h. das Delegatde�niert Manager als gefordertes Argument und akzeptiert alle Methoden, die Manager oder einen Basistypvon Manager als Argument erwarten. Dies läuft unter dem Begri� Contravariance.

Wird eine Funktion/Methode nur für ein Delegat benötigt, dann kann diese auch direkt als anonyme Funktiondem Delegat übergeben werden:

delegate void TestDelegate(string s);

TestDelegate testDelB = delegate(string s) { Console.WriteLine(s); };

Manche sprechen hierbei auch von anonymen Delegaten, wobei ich dies eher irreführend �nde. Neben der hierdargestellten Schreibweise gibt es eine weitere, noch kürzere Darstellung, die sich Lambda-Ausdruck nennt, aufdie wir in Abschnitt ?? auf Seite ?? noch einmal eingehen werden:

delegate void TestDelegate(string s);

TestDelegate testDelC = (x) => { Console.WriteLine(x); };

Delegates werden sehr häu�g für Sortierfunktionen verwendet, wie folgendes Beispiel von Stackover�ow.comzeigt:

public string SortByDateModified(List<CartItem> items)

{

items.Sort(delegate(CartItem itemA, CarItem itemB)

{

return itemA.DateModified.CompareTo(itemB.DateModified);

});

}

47 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 48: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

Wann können Delegate eingesetzt werden? Immer dann, wenn mehr als eine konkrete Implementation einerMethode relevant ist. Delegate sind somit auf Methodenebene mit Schnittstellen (Interfaces) auf Klassenebenevergleichbar. Sie können auch immer dann verwendet werden, wenn über ein Interface mit nur einer Methodenachgedacht wird. Sie können zur Umsetzung folgender Entwurfsmuster verwendet werden: Adapter, Command,Mediator, Chain, Observer, Visitor. Seit 2005 sind Delegate laut Microsoft genauso performant wie Schnittstellen(Interfaces). Delegate sind natürlich Referenztypen, die auf dem Heap liegen.

Frage 3.20Finden Sie drei weitere Anwendungsbeispiele für Delegates.

Ein Delegat kann entweder (wie in diesem Beispiel) synchron oder mithilfe der BeginInvoke-Methode und derEndInvoke-Methode asynchron aufgerufen werden.

3.7.1 Generische Delegates

In .Net gibt es generische Listen, Methoden, Klassen, Schnittstellen und auch generische Delegate.

public delegate void Del<T>(T item);

public static void Notify(int i) { }

Del<int> m1 = new Del<int>(Notify);

Zeile 1 de�niert ein generisches Delegat. Erst bei der Instanzierung in Zeile 3 wird der Typ festgelegt.

Durch die Einführung von generischen Delegattypen im Namensraum System (ab .Net 2.0) werden eigeneDelegatde�nitionen (weitestgehend) über�üssig:

public delegate TResult Func<in T, out TResult>(T arg)

public delegate void Action<in T>(T obj)

Func ist also der Delegattyp zur Aufnahme von Methoden mit Rückgabewert und Action für Methoden ohneRückgabewert. Ab .Net 4.0 gibt es beide Delegate mit bis zu 10 Argumenten. Damit sollte sich jeder Delegattypde�nieren lassen (von .Net-Attributen abgesehen).

Es gibt auch vorde�nierte generische Delegate, die bereits mit geeigneten Attributen versehen sind, wie z.B.AppDomainInitializer-Delegat. Oder für den häu�gen Anwendungsbereich Sortieren mit Delegaten, gibt esComparison<T>-Delegat.

Zur Veranschaulichung, betrachte ich ein Beispiel aus dem empfehlendswerten Buch Kompaktkurs C# 4.0, wiemit Hilfe des Func-Delegats auf der Konsole eine mathematische Funktion geplottet werden kann.

Anstelle der eigenen De�nition

delegate double Function(double x);

Verwenden wir

Func<double, double>

Hier das vollständige Programm, das einmal einen Lambda-Ausdruck (Zeile 6) an die Funktion Plot übergibtund einmal eine statische Funktion (Zeile 8).

48 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 49: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

class Program

{

static void Main(string[] args)

{

Console.WriteLine("Linear:");

Plot(x => x);

Console.WriteLine("\nParabel:");

Plot(Parabel);

Console.ReadLine();

}

static double Parabel(double x){return x*x;}

static void Plot(Func<double, double> f)

{

//−−− create a 50 x 50 drawing plane

char[,] dot = new char[50, 50];

for (int i = 0; i < dot.GetLength(0); i++)

for (int j = 0; j < dot.GetLength(1); j++)

dot[i, j] = ' ';

//−−− compute the function

int height = dot.GetLength(0);

for (int x = 0; x < dot.GetLength(1); x++)

{

int y = (int)f(x);

if (0 <= y && y < height)

{

dot[(height - 1) - y, x] = '*';

}

}

//−−− draw the plot

for (int i = 0; i < dot.GetLength(0); i++)

{

Console.Write('|');

for (int j = 0; j < dot.GetLength(1); j++)

Console.Write(dot[i, j]);

Console.WriteLine();

}

for (int i = 0; i < dot.GetLength(1); i++)

Console.Write('-');

}

}

Frage 3.21Decken die vorde�nierten Delegates alle Anwendungsfälle für Delegates ab, d.h. ist es nicht mehr notwendigeigene Delegates zu de�nieren?

Wie zu erwarten, gilt auch für generische Delegate die Aussage bzgl. Kontravarianz und Kovarianz, wie imersten Teil bereits dargestellt. Generische Delegate sind ja eigentlich nichts Besonderes, schlieÿlich werden beider Deklaration der Delegatvariablen konkrete Datentypen angegeben. Die Kombination von Kovarianz undKontravarianz veranschaulicht folgendes Codefragment, das ich von Microsoft übernommen habe.

public class Base {}

public class Derived : Base {}

public static Derived MyMethod(Base b)

{

return b as Derived ?? new Derived();

}

// Covariant return type and contravariant parameter type.

Func<Derived, Base> f = MyMethod;

Base b = f(new Derived());

Frage 3.22Was liefert folgender Code und warum?

var contents = new List<Func<int>>();

var s = new StringBuilder();

for (var i = 4; i < 7; i++)

contents.Add(() => i);

49 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 50: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

for (var k = 0; k < contents.Count; k++)

s.Append(contents[k]());

Console.WriteLine(s);

Was ist hier anders?

var contents = new List<Func<int>>();

var s = new StringBuilder();

for (var i = 4; i < 7; i++)

{

var j = i;

contents.Add(() => j);

}

for (var k = 0; k < contents.Count; k++)

s.Append(contents[k]());

Console.WriteLine(s);

3.7.2 Mulitcastdelegaten

Eine nützliche Eigenschaft von delegate-Objekten besteht darin, dass sie mithilfe des Operators + einer Dele-gatinstanz als Multicast zugewiesen werden können. Ein zusammengesetzter Delegat ruft die beiden Delegatenauf, aus denen er besteht. Es können ausschlieÿlich Delegaten mit demselben Typ kombiniert werden.

Mithilfe des Operators - kann ein Komponentendelegat aus einem zusammengesetzten Delegaten entfernt wer-den. Das folgende Beispiel zeigt die Verwendung dieser Operatoren.

Listing 3.28: Multicastdelegaten

delegate void Del(string s);

class TestClass

{

static void Hello(string s)

{

System.Console.WriteLine(" Hello, {0}!", s);

}

static void Goodbye(string s)

{

System.Console.WriteLine(" Goodbye, {0}!", s);

}

static void Main()

{

Del a, b, c, d;

a = Hello;

b = Goodbye;

c = a + b;

d = c - a;

System.Console.WriteLine("Invoking delegate a:");

a("A");

System.Console.WriteLine("Invoking delegate b:");

b("B");

System.Console.WriteLine("Invoking delegate c:");

c("C");

System.Console.WriteLine("Invoking delegate d:");

d("D");

}

}

Frage 3.23Was gibt das Programm Multicastdelegaten aus?

50 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 51: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

3.7.3 Ereignisse

Delegate sind in .Net die Basis für Events (Ereignisse). Events sind die Technologie, wie verschiedene Kom-ponenten eines Programms und insbesondere einer GUI miteinander kommunizieren. Klickt ein Anwender aufeinen Knopf, so ändert sich der Zustand dieses Knopfes (er ist gedrückt) und alle, die sich dafür interessierenwerden darüber informiert � alle Zuhörer (Listener) können entsprechend reagieren. Z.B. kann nun ein Datensatzin die Datenbank geschrieben werden. Java bildet dies in entsprechenden Klassen ab. .Net tut dies �versteckt�mit besonderen Delegaten. Warum ich hier �versteckt� schreibe hat den Grund, dass normale Delegatvariablennach auÿen hin �zu� sichtbar sind. Dies soll folgender Abschnitt erläutern.

Ereignisse mit �normalen� Delegatvariablen: Gefährliches Lotto Betrachten wir zunächst die Möglichkeit,Ereignisse mit Delegatvariablen umzusetzen. .Net bietet uns eine für Ereignisse nützliche Delegatde�nition:

public delegate void EventHandler (Object sender,EventArgs e)

Und als generische Version, mit eigenen Argumenten:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

In einer Klasse soll eine Zustandsänderung (die Ziehung neuer Lottozahlen) per Delegat allen Interessentenmitgeteilt werden, also z.B.

public class Lotto

{

public EventHandler<LottoEventArgs> LottoAmMittwoch;

public void Ziehung()

{

if (LottoAmMittwoch != null)

LottoAmMittwoch(this, new LottoEventArgs(new List<int> { 8, 4, 36, 1, 28, 2 }));

}

}

public class LottoEventArgs: EventArgs

{

public LottoEventArgs(List<int> ziehung)

{

if (ziehung.Count != 6)

throw new ArgumentException("needs exactly 6 integers", "ziehung");

this.Ziehung = ziehung;

}

public List<int> Ziehung;

}

Ein Kiosk kann zwei Spieler anmelden:

public class Spieler

{

public string Name;

public List<int> Tipp;

public Spieler(string name, List<int> tipp)

{

this.Name = name;

this.Tipp = tipp;

}

public void Auswerten(Object sender, LottoEventArgs eargs)

{

int countWon = 0;

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

if (eargs.Ziehung.Contains(Tipp[i]))

countWon++;

Console.WriteLine(string.Format("{0} hat {1:d} Richtige", Name, countWon));

}

}

public class Kiosk

{

Lotto lotto;

public Kiosk(Lotto lotto)

{

this.lotto = lotto;

51 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 52: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

}

public void Anmeldungen()

{

Spieler gierigerSpieler = new Spieler("Dagobert", new List<int> { 1, 2, 3, 4, 5, 6 });

lotto.LottoAmMittwoch += gierigerSpieler.Auswerten;

Spieler normalerSpieler = new Spieler("Otto", new List<int> { 2, 4, 6, 8, 10, 12 });

lotto.LottoAmMittwoch += normalerSpieler.Auswerten;

}

}

Und das Programm nimmt die Anmeldungen entgegen und führt die Ziehung durch:

Lotto lotto = new Lotto();

Kiosk kiosk1 = new Kiosk(lotto);

kiosk1.Anmeldungen();

lotto.Ziehung();

Dies führt zur Ausgabe:

Dagobert hat 3 Richtige

Otto hat 3 Richtige

Bei ö�entlichen Delegaten, wie hier verwendet und zur Anmeldung durch den Kiosk benötigt, kann der Kio-skbesitzer aber auch mit Dagobert gemeinsame Sache machen und folgende Zeilen einfügen:

//Ziehung manipulieren

lotto.LottoAmMittwoch = gierigerSpieler.Auswerten;

lotto.LottoAmMittwoch(lotto, new LottoEventArgs(gierigerSpieler.Tipp));

Damit wird nur die Auswertung von Dagobert aufgerufen (alle weiteren bis dahin registrierten Methoden werdendurch diese eine Methode überschrieben) und LottoAmMittwoch wird mit den Zahlen von Dagobert aufgerufen.Dies führt zur Ausgabe:

Dagobert hat 6 Richtige

Dagobert hat 3 Richtige

Die erste Zeile erfolgt aus der Manipulation, dass LottoAmMittwoch direkt aufgerufen wird und die zweite Zeileresultiert aus dem Aufruf lotto.Ziehung() im eigentlichen Programm. Wir stehen also vor dem Problem, dasses einerseits von auÿen möglich sein soll, sich zu registrieren und zu deregistrieren. Auf der anderen Seite solles aber nicht möglich sein, die Registrierung von anderen zu überschreiben oder das Ereignis selbst auszulösen.Man kann dies selbst programmieren, in dem man Methoden Subscribe und Unsubscribe ö�entlich macht unddas Delegat selbst privat oder dieses Pattern .Net überlässt und das Schlüsselwort event verwendet. DieseMöglichkeit zeigt der nächste Abschnitt. Delegatvariablen und das Schlüsselwort event

public class Lotto

{

public event EventHandler<LottoEventArgs> LottoAmMittwoch;

Damit ist ein Registrieren und Deregistrieren weiter möglich, aber der Betrugsversuch nicht mehr.

Events mit IL-Disassembler IL-Disassembler zeigt uns, was in beiden Fällen erzeugt wird. Für die Delegat-variable, erhalten wir:

.field public class [mscorlib]System.EventHandler`1<class GenericDelegate.LottoEventArgs> LottoAmMittwoch

Während die Event-Deklaration dies erzeugt:

.event class [mscorlib]System.EventHandler`1<class GenericDelegate.LottoEventArgs> LottoAmMittwoch

{

.addon instance void GenericDelegate.Lotto::add_LottoAmMittwoch(class [mscorlib]System.EventHandler`1<class

GenericDelegate.LottoEventArgs>)

.removeon instance void GenericDelegate.Lotto::remove_LottoAmMittwoch(class [mscorlib]System.EventHandler`1<

class GenericDelegate.LottoEventArgs>)

}

52 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 53: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

Leider hat hier die GUI von IL-Disassembler schon wieder das Schlüsselwort event reingeschmuggelt, denn begibtman sich auf die Kommandozeile mit:

ildasm "TestDelegate.exe" /out= TestDelegate.il

Dann erhält man:

.field private class [mscorlib]System.EventHandler`1<class GenericDelegate.LottoEventArgs> LottoAmMittwoch

.method public hidebysig specialname instance void

add_LottoAmMittwoch(class [mscorlib]System.EventHandler`1<class GenericDelegate.LottoEventArgs> 'value') cil

managed

{

Also eigentlich wird auch hier ein �eld erzeugt, aber erstens ist dies private und nicht public wie oben undzweitens werden ö�entliche Methoden zum Registrieren und Deregistrieren erzeugt. Genau das Pattern, was wiram Ende des letzten Abschnitts vorgeschlagen haben.

Ein typisches Event-Beispiel Die bisherigen Ausführungen dienten dazu, den Unterschied zwischen einer alsevent deklarierten Variablen und einer gewöhnlichen Delegatvariablen herauszuarbeiten. Abschlieÿend betrach-ten wir noch ein typisches Beispiel aus der GUI-Programmierung. Wenn Sie beispielsweise einen Button auf dieOber�äche ziehen und diesen Doppelklicken, dann wird sichtbar folgender Code erzeugt:

private void button1_Click(object sender, EventArgs e)

{

//Ereignisroutine für das Drücken des Buttons

}

Im Hintergrund wird zusätzlich folgender Code erzeugt:

this.button1.Click += new System.EventHandler(this.button1_Click);

was man auch kürzer als

this.button1.Click += this.button1_Click;

schreiben könnte. Die Methode �button1_Click� wird also registriert. Markiert man Click und drückt F12,erscheint die zugehörige De�nition:

public class Control ...

{...

public event EventHandler Click;

protected virtual void OnClick(EventArgs e);

Wobei Button von ButtonBase und diese wiederum von Control erbt. Innerhalb einer Klasse (hier Buttonbzw. Control) wird also ein event de�niert (hier Click) und für dieses Ereignis einer entsprechenden Instanzdieser Klasse (hier button1), kann eine Methode registriert werden (hier �button1_Click�). Die zugehörigeNamenskonvention lässt sich auch sehr schön ableiten. CustomControl Submit

Im Allgemeinen haben wir also eine Klasse, die ein Event (MyEvent) mit eigenen Argumenten (MyEventArgs� siehe De�nition von LottoEventArgs) de�niert:

public class MyEventArgs : EventArgs { }

class MyGUIElement : Control

{

public event EventHandler<MyEventArgs> MyEvent;

protected virtual void OnMyEvent(MyEventArgs myevent)

{

if (MyEvent != null)

{

MyEvent(this, myevent); // Notify Subscribers

}

}

public void PerformMyEvent()

{

//possible validation

OnMyEvent(new MyEventArgs());

53 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 54: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

}

}

Wir erzeugen eine Instanz dieser Klasse:

private MyGUIElement myGUIElement;

myGUIElement = new MyGUIElement();

Und registrieren eine entsprechend de�nierte Methode:

myGUIElement.MyEvent += myGUIElement_MyEvent;

private void myGUIElement_MyEvent(object sender, EventArgs e)

{

MessageBox.Show("myevent was raised");

}

Jetzt muss dieses Ereignis durch irgendetwas ausgelöst werden, z.B. durch ein Klick, also muss für Click eineMethode registriert werden, die MyEvent auslöst:

myGUIElement.Click += myGUIElement_Click;

private void myGUIElement_Click(object sender, EventArgs e)

{

//raise submit

myGUIElement.PerformMyEvent();

}

Anschaulicher wird dies im konkreten Fall eines CustomControls. Der Code basiert auf einem Artikel vonarcadia.

public class SubmitButtonControl : System.Windows.Forms.UserControl

{

private System.Windows.Forms.TextBox txtName;

private System.Windows.Forms.Button btnSubmit;

...

//use prede�ned delegate Action

public event Action Submit;

protected virtual void OnSubmit()

{

if (Submit != null)

{

Submit(); // Notify Subscribers

}

}

private void btnSubmit_Click(object sender, System.EventArgs e)

{

if (txtName.Text.Length == 0)

{

//noch schöner mit ErrorProvider!

MessageBox.Show("Please enter your name.");

}

else

{

//submit Ereignis auslösen

OnSubmit();

}

}

public string UserName

{

get { return txtName.Text; }

set { txtName.Text = value; }

}

}

Frage 3.24Beschreiben Sie die Konzepte �delegate� und �event� und das Zusammenspiel sowie mögliche Gemeinsamkeitenund Unterschiede.

54 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 55: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

3.7.4 Asynchrone Delegates

Asynchrone Programmierung ist ein schwer abzugrenzendes Thema.

In Bezug auf Delegates fangen wir mit einem einfachen Beispiel an: Während im Hintergrund beispielsweise dieFestplatte durchsucht wird, läuft das Hauptprogramm weiter. An einer fest gelegten Stelle im Hauptprogrammwird das Ergebnis der Suche verarbeitet. Entweder die Suche ist dann schon abgeschlossen, oder das Programmwartet nun, bis dies geschehen ist (Zeile 32). Wir de�nieren also wieder ein Delegate (Zeile 1), instanziieren es miteiner der Signatur entsprechenden Methode (Zeile 24) und mit BeginInvoke (Zeile 25) leiten wir den asynchronenAufruf ein. Mit EndInvoke (Zeile 32) warten wir auf sein Ende. Danach können wir den Rückgabewert (die Listeder gefundenen Dateien) verarbeiten.

delegate List LongProcessCaller(string name);

public class Test

{

public List Search(string path)

{

List result = new List();

Console.WriteLine("async: start long search process in " + path);

for (int i = 0; i < 2; i++)

{

System.Threading.Thread.Sleep(50);

result.Add("filename " + i.ToString());

Console.WriteLine("async: found one file");

}

Console.WriteLine("async: finnished long search process...");

return result;

}

}

class Program

{

static void Main(string[] args)

{

Test t = new Test();

Console.WriteLine("Hello this is the main program, ...");

LongProcessCaller caller = new LongProcessCaller(t.Search);

IAsyncResult result = caller.BeginInvoke(@"c:\tmp", null, null);

for (int i = 0; i < 3; i++)

{

Console.WriteLine("main:I work too ...");

System.Threading.Thread.Sleep(25);

}

Console.WriteLine("main: wait for long process to finnish");

List found = caller.EndInvoke(result);

Console.WriteLine("main:found the following items: ");

foreach (string filename in found)

Console.WriteLine(filename);

Console.ReadLine();

}

}

Jetzt wartet das Programm zwar nicht direkt beim Aufruf der Routine, aber doch ggf. später. Schöner wäre es,wenn die Verarbeitung des Ergebnisses der Suche dann erfolgen könnte, wenn die Suche beendet ist. Dies istdurch Übergabe einer Methode bei BeginInvoke (Zeile 26) möglich:

55 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 56: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

1 // restlicher Code wie oben

2 IAsyncResult result = caller.BeginInvoke(@"c:\tmp", new AsyncCallback(MyCallback), null);

3 // ... EndInvoke nicht hier!

4 static void MyCallback(IAsyncResult async)

5 {

6 System.Runtime.Remoting.Messaging.AsyncResult ar = (System.Runtime.Remoting.Messaging.AsyncResult) async;

7 LongProcessCaller caller = (LongProcessCaller)ar.AsyncDelegate;

8 List found = caller.EndInvoke(async);

9 Console.WriteLine("callback:found the following items: ");

10 foreach (string filename in found)

11 Console.WriteLine(filename);

12 }

Um an das eigentliche Ergebnis zu kommen, muss man trickreich casten, aber die Codezeilen lassen sich leichtauf andere Probleme übertragen.

Auf diese Art und Weise lieÿen sich viele Hintergrundarbeiten gleichzeitig starten:

for (int i = 0; i < 50; i++)

{

IAsyncResult result = caller.BeginInvoke(i.ToString(), new AsyncCallback(MyCallback), null);

}

Mit BeginInvoke wird ein Thread im ThreadPool gestartet. Die Anzahl der möglichen Threads im Threadpoolist beschränkt, ggf. landet ein Thread dann in der Warteschlange.

Dies ist nur ein sehr kurzer Einstieg in die asynchrone Programmierung und ich beschränke mich in dieser Stelleauch nur auf die einfache und direkte Möglichkeit, die Delegate bieten. Mit der neuen .Net-Sprache F# oderden Task-Klassen bieten sich viel mehr Möglichkeiten. Mehr zu asynchroner oder paralleler Programmierung(hier tut sich gerade im .Net-Umfeld sehr viel) �nden Sie hier:

• Threading in C#

• Parallel Programming with Microsoft .NET

• Multithreading, Delegates, and Custom Events (mit UI)

• Scalable Multithreaded Programming with Tasks

3.7.5 LINQ

In einem datenbankbasierten Projekt mussten wir häu�g kompliziertere Abfragen zusammen bauen. Egal, inwelcher Programmiersprache, läuft dies normalerweise darauf hinaus, sich entsprechend parametrisierte Stringszusammen zu setzen. Schnell vergisst man eine Klammer, schlieÿt den String nicht oder macht andere Syn-taxfehler. Ein automatischer Test zeigt diese zwar direkt auf, aber die Entwicklung ist umständlich, die Fehlerschwer zu �nden. Mit LINQ (language integrated query) bietet .Net eine an SQL angelehnte Abfragesprache,mit Syntax-Check durch den Compiler. Folgendes Beispiel zeigt eine einfache LINQ-Abfrage:

//Code 1

List members = new List();

members.Add(new Member(30, "Tim"));

members.Add(new Member(5, "Struppi"));

members.Add(new Member(53, "Kapität Haddock"));

members.Add(new Member(60, "Prof. Bienlein"));

IEnumerable oldies = from m in members where m.Age > 50 select m.Name;

foreach(string name in oldies)

Console.WriteLine(name);

56 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 57: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

3.7.6 Erweiterungsmethoden

Mit Hilfe des ObjektBrowsers können Sie sich leicht die De�nition für where ansehen (F12 funktioniert leidernicht):

//Namespace System.Linq.Enumerable

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate

);

Erweiterungsmethoden wurden mit .Net 3.5 eingeführt. Mit ihnen kann von auÿen, sozusagen im NachhineinFunktionalität zu vorhandenen Klassen hinzugefügt werden. Func ist ein vorde�niertes Delegate. Noch mehrHintergrundinformation erhält man mit dem Re�ector und Disassembling:

//Code 2

IEnumerable oldies = members.Where(delegate (Member m) {

return (m.Age > 50);

}).Select(delegate (Member m) {

return m.Name;

});

Mit Lambda lieÿe sich dies auch umschreiben in:

//Code 3

IEnumerable oldies = members.Where(m => m.Age > 50).Select(m => m.Name);

Disassembling liefert hier, wie nicht anders zu erwarten, das in Code 2 aufgeführte Ergebnis. In dieser Schreib-weise funktioniert auch ein F12 auf dem markierten Where.

Merke 8 (LINQ)Das Ergebnis einer LINQ-Abfrage ist keine Liste, sondern �IEnumerable� auf Basiseines Delegates.

3.7.7 Abfragen

Unter 101 LINQ Samples �nden Sie viele weitere unterschiedliche LINQ-Beispiele. Die Schlüsselwörter undMöglichkeiten sind mit SQL vergleichbar. Hier ist eine kleine Auswahl dieser Beispiele:

Listing 3.29: LINQ-Beispiele

//Select−Beispielint[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

string[] strings = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };

var textNums =

from n in numbers

select strings[n];

//Count−Beispielint[] factorsOf300 = { 2, 2, 3, 5, 5 };

int uniqueFactors = factorsOf300.Distinct().Count();

Console.WriteLine("There are {0} unique factors of 300.", uniqueFactors);

//Order−Beispieldouble[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };

var sortedDoubles =

from d in doubles

orderby d descending

select d;

var doublesArray = sortedDoubles.ToArray();

//Join−Beispielstring[] categories = new string[]{"Beverages", "Condiments", "Vegetables", "Dairy Products"};

List<Product> products = GetProductList();

var q = from c in categories

join p in products on c equals p.Category into ps

select new { Category = c, Products = ps };

57 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 58: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.7 Delegates

Frage 3.25Welche zugrundeliegende Struktur können Sie bei den aufgeführten Beispielen entdecken?

In den oben aufgeführten Beispielen wird der LINQ-Ausdruck immer dem implizit typisierten Typ �var� zuge-ordnet. Var ist ein Typ, der dem Compiler sagt, den dafür passenden Typ einzusetzen, der eigentliche Typ wirdalso zur Compile-Zeit festgelegt, nicht zur Laufzeit. Der tatsächliche Typ ist, wie in Abschnitt 3.7.5 auf Seite55 eingeführt �IEnumerable<TSource>�. Übergeben wird ein Delegate, also ein Methodenzeiger. Das Ergebniseiner LINQ-Abfrage ist also nicht, wie vielleicht zu erwarten, eine Liste oder ähnliches, sondern ein dynamischerAusdruck, der erst bei Zugri� auf ein konkretes Element, wie beim Durchlaufen einer foreach-Schleife oder derMethode �ToList� ausgewertet wird!

Betrachten Sie hierzu folgendes Beispiel:

Listing 3.30: LINQ-Falle

int[] data={1,2,3,1,2,1};

var list = new List<IEnumerable<int>>();

foreach (var in in data.Distinct())

list.Add(from k in data where k==i select k);

foreach (var m in list)

foreach (var n in m)

Console.Write(n);

Frage 3.26Was wird hier ausgegeben und warum? Welche Parallele sehen Sie zu Frage 3.22?

(1) 111223

(2) 123

(3) 333

(4) keine der genannten

58 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 59: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

3.8 P3* - Delegates und Events

3.8 P3* - Delegates und Events

Abgabetermin: 17.04.2011

Aufgabe P3.1Ziel des heutigen Praktikums ist es, den nicht GUI-basierten Teil der Playlist zu implementieren.

(a) Implementieren Sie geeignete Sortiermethoden mit Hilfe von Delegates.

(b) Schreiben Sie die Sortiermethoden unter Verwendung von anonyme Methoden.

(c) Implementieren Sie alle weiteren nicht GUI-basierten Methoden.

(d) Erzeugen Sie ein Klassendiagramm Ihrer Applikation.

(e) Erzeugen Sie ein Sequenz-Diagramm.

Aufgabe P3.2Erläutern Sie folgenden Code. Legen Sie eine Wertetabelle mit n = 1,2,3 und xxx an. Was wird hier berechnet?

Func<int, int> xxx = null;

xxx = (n) => n <= 1 ? 1 : n + xxx(n - 1);

Aufgabe P3.3Erstellen Sie eine Liste von Personen mit den Eigenschaften Name und Alter. Erstellen Sie dazugehörige LINQ-Ausdrücke, die

(a) Den Namen aller Personen unter 18 anzeigt.

(b) Ein Array mit allen Personen über 18 erzeugt.

Aufgabe P3.4Wählen Sie ein Pattern, das in http://www.cs.dartmouth.edu/~sensorlab/pubs/SL-HotEmnets08.pdf

dargestellt wird.

(a) Beschreiben Sie kurz den Zweck des Patterns anhand eines geeigneten Beispiels.

(b) Implementieren Sie dieses Pattern in einer Nicht-.Net-Programmiersprache Ihrer Wahl.

(c) Diskutieren Sie die Unterschiede der Implementation der Teilaufgabe (b) mit der im Paper dargestelltenImplementation.

3.8.1 Links

Dieses Kapitel soll Ihnen einen Schnellstart in C# ermöglichen. Es ist bewusst kurz gehalten. AusführlichereInformationen und viele Codebeispiele �nden Sie unter folgenden Links:

• Programmierhandbuch von Microsoft

• codeplex - .Net open source, insbesondere die Code-Snippets All-In-One Code Framework

• Viele Artikel und Beispielprojekte von .Net-Entwicklern: The Code Project

• Deutschsprachiges Tutorial: guide to C# von Golo Roden

• C# für Umsteiger: Learning C# from Java and C++

59 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 60: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

4 O�ce-Entwicklung

Lernziele:

• Zugrundeliegende Technologie von O�ce-Applikationen kennen

• Applikationen, die O�ce-Dokumente erstellen oder editieren, entwickeln können

• Eigene O�ce-Add-Ins entwickeln können

Mit VS2010 ist die Kommunikation mit O�ce einfacher geworden. Das Framework übernimmt die Kom-munikation und stellt das komplette Objektmodell von beispielsweise Excel zur Verfügung. Leider erfolgtdie Kommunikation weiterhin über COM-Interop. Erstellen Sie ein neues Excel-Addin und anschlieÿendeine Dependency-Diagram (auf Basis von Namespace oder Assembly). Sie erhalten eine Graphik ähnlich zuAbbildung 4.1.

Abbildung 4.1: Excel-Addin: Interop

Vor .Net 4.0 musste man mit jedem Projekt eine recht groÿe Interop-DLL, die das gesamte Interface enthieltmitliefern. Jetzt, kann Interop, reduziert auf die tatsächlich genutzten Teile, direkt in die eigene Assemblyreinkompiliert werden.

4.1 O�ce-Dokumente von auÿerhalb verändern oder erstellen

Im TrainingKit �nden Sie einige nützliche Labs und damit Beispiele, wie Sie Excel von C# aus steuern können.Folgendes Beispiel startet Excel und fügt Werte in eine neue Arbeitsmappe ein. Die Applikation selbst ist alsConsolen-Applikation entwickelt.

public class Account

{

public int ID { get; set; }

public double Balance { get; set; }

public string AccountHolder { get; set; }

}

Listing 4.1: O�c-Werte in Excel einfügen

static void Main(string[] args)

{

var checkAccounts = CreateAccountList();

checkAccounts.DisplayInExcel((account, cell) =>

60

Page 61: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

4.2 Excel-Addin

{

cell.Value2 = account.ID;

cell.get_Offset(0, 1).Value2 = account.Balance;

cell.get_Offset(0, 2).Value2 = account.AccountHolder;

if (account.Balance < 0)

{

cell.Interior.Color = 255;

cell.get_Offset(0, 1).Interior.Color = 255;

cell.get_Offset(0, 2).Interior.Color = 255;

}

});

}

private static List<Account> CreateAccountList()

{

var checkAccounts = new List<Account> {

new Account{

ID = 1,

Balance = 285.93,

AccountHolder = "John Doe"

},

new Account {

ID = 2,

Balance = 2349.23,

AccountHolder = "Richard Roe"

},

new Account {

ID = 3,

Balance = -39.46,

AccountHolder = "I Dunoe"

}

};

return checkAccounts;

}

static void DisplayInExcel(this IEnumerable<Account> accounts,

Action<Account, Excel.Range> DisplayFunc)

{

var x1 = new Excel.Application();

//see the Note below

x1.Workbooks.Add();

x1.Visible = true;

x1.get_Range("A1").Value2 = "ID";

x1.get_Range("B1").Value2 = "Balance";

x1.get_Range("C1").Value2 = "Account Holder";

x1.get_Range("A2").Select();

foreach (var ac in accounts)

{

DisplayFunc(ac, x1.ActiveCell);

x1.ActiveCell.get_Offset(1, 0).Select();

}

((Excel.Range)x1.Columns[1]).AutoFit();

((Excel.Range)x1.Columns[2]).AutoFit();

((Excel.Range)x1.Columns[3]).AutoFit();

}

4.2 Excel-Addin

Zur Erstellung eines Excel-Addins gibt es eine entsprechende Vorlage in VS2010. Ein Excel-Add-In be�ndetsich in Excel, d.h. es läuft innerhalb von Excel. Daher ist es im Gegensatz zum letzten Beispiel nicht mehrnotwendig, eine neue Excel-Applikation zu erstellen. Folgendes Code-Beispiel zeigt Ihnen, wie Sie einen Wert ineine Zelle einfügen und auslesen können.

private void ThisAddIn_Startup(object sender, System.EventArgs e)

{

Excel.Workbook wb = this.Application.Workbooks[1];

61 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 62: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

4.2 Excel-Addin

Excel.Worksheet ws = wb.Worksheets[1];

var cell1 = ws.get_Range("A1", Type.Missing);

cell1.Value2 = 2;

var cell2 = ws.get_Range("A2", Type.Missing);

cell2.Value2 = cell1.Value2 * 42;

}

Mit dem �Managed Extensibility Framework (MEF)� können Sie ihre eigenen Applikationen ähnlich für Add-Insö�nen.

62 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 63: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

5 Datenbankanbindung

Lernziele:

• Daten aus einer Datenbank auslesen und schreiben können

• Parametrisierte SQL-Befehle ausführen können

• LINQ-Abfragen auf eine Datenbank anwenden können

• Eine geeignete Zugri�smöglichkeit auf eine Datenbank im jeweiligen Kontext auswählen können

Die Basistechnologie aller .Net-Datenbankzugri�e heiÿt ADO.Net. Diese steht im Fokus dieser Veranstaltung.ADO.Net bietet verschiedene Provider zum Zugri� auf Datenbanken wie SQL Server, Oracle oder über OLE.Dbund ODBC auf unterschiedliche Datenquellen. XML wird als Datenquelle zunehmend bedeutender, so dass auchhier ein performanter lesender, suchender und schreibender Zugri� nötig ist. Über ADO.Net gibt es passendeInterfaces, man muss jedoch für SQL Server und Oracle unterschiedliche Provider verwenden.

5.1 .NET Framework-Datenanbieter

Die .NET Framework-Datenanbieter sind Komponenten, die explizit für die Datenbearbeitung und den schnel-len, vorwärts gerichteten, schreibgeschützten (d.h. nur lesenden) Zugri� auf Daten entworfen wurden. DasConnection-Objekt sorgt für eine Verbindung mit einer Datenquelle. Mit dem Command-Objekt könnenSie auf Datenbankbefehle de�nieren (ggf. parametrisiert), um Daten zurückzugeben oder zu ändern undgespeicherte Prozeduren (stored procedures) auszuführen. Zum Aufbau einer Datenbankverbindung brau-chen Sie den sogenannten �Connectionstring�. Diese kann man sich mit der IDE erzeugen oder unter http:

//www.connectionstrings.com nachschlagen.

Listing 5.1: Datenzugri� mit DataReader

string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;";

//mit using wird Connection und DataReader immer geschlossen!

using (SqlConnection conn = new SqlConnection(connString))

{

SqlCommand cmd = conn.CreateCommand();

cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers";

conn.Open();

Abbildung 5.1: DB: Technologien

63

Page 64: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

5.2 DataSet

Abbildung 5.2: Verbindungsorientierter DB-Zugri�

using (SqlDataReader dr = cmd.ExecuteReader())

{

if (myReader.HasRows)

while (dr.Read())

Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1));

dr.close();

}

//nur einen Wert auslesen:

SqlCommand ordersCMD = new SqlCommand("SELECT Count(*) FROM Orders", nwindConn);

Int32 count = (Int32)ordersCMD.ExecuteScalar();

//mit Parametern

int catId = 1;

cmd = new SqlCommand("SELECT CategoryID, CategoryName FROM Categories WHERE CategoryID = @CategoryID", conn);

cmd.Paramters.AddWithValue("@CategoryID, catId);

//reader wie gehabt

//mit Command-Parametern

string sql = "SELECT * FROM users WHERE username=@username AND password=@password";

cmd = new SqlCommand(sql, conn);

cmd.Parameters.Add("@username", SqlDbType.NVarChar, 16);

cmd.Parameters.Add("@password", SqlDbType.NVarChar, 16);

cmd.Parameters.Value["@username"] = username;

cmd.Parameters.Value["@password"] = password;

//reader wie gehabt

conn.Close();

}

5.2 DataSet

Das DataSet, ein aus einer Datenquelle abgerufener Datencache im Arbeitsspeicher, Das DataSet besteht auseiner Au�istung von DataTable-Objekten, die Sie mit DataRelation-Objekten aufeinander beziehen können.In einer typischen Implementierung mit mehreren Ebenen werden die folgenden Schritte zum Erstellen undAktualisieren eines DataSet und zum anschlieÿenden Aktualisieren der ursprünglichen Daten ausgeführt:

1Das DataSet besteht aus einer Au�istung von DataTable-Objekten, die Sie mit DataRelation-Objekten auf-einander beziehen können.

1. Erstellen Sie mithilfe eines DataAdapter jede DataTable in einem DataSet, und füllen Sie sie mit Datenaus einer Datenquelle.

1http://msdn.microsoft.com/de-de/library/system.data.dataset.aspx

64 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 65: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

5.3 DataSet oder DataReader

Abbildung 5.3: Verbindungsloser DB-Zugri�

2. Ändern Sie die Daten in einzelnen DataTable-Objekten, indem Sie DataRow-Objekte hinzufügen, aktua-lisieren oder löschen.

3. Rufen Sie die GetChanges-Methode auf, um ein zweites DataSet zu erstellen, das nur die Änderungen anden Daten darstellt.

4. Rufen Sie die Update-Methode von DataAdapter auf, und übergeben Sie das zweite DataSet als Argument.

5. Rufen Sie die Merge-Methode auf, um die Änderungen aus dem zweiten DataSet mit dem ersten zusam-menzuführen.

6. Rufen Sie AcceptChanges für das DataSet auf. Sie können auch RejectChanges aufrufen, um die Ände-rungen zu verwerfen.

Listing 5.2: Update mit DataSet

//connect to db

SqlCommand mySelectCommand = New SqlCommand("select * from customers", myConnection);

SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter(mySelectCommand);

DataSet myDataSet = new DataSet();

mySqlDataAdapter.Fill(myDataSet,"Customers");

//anzeigen

foreach (DataRow myDataRow in myDataSet.Tables["Customers"].Rows)

{

Console.WriteLine(myDataRow["CustomerId"].ToString());

}

//löschen

myDataSet.Tables["Customers"].Rows[0].Delete();

//update

myDataSet.Tables["Customers"].Rows[0]["ContactName"]="Peach";

mySqlDataAdapter.Update(myDataSet);

5.3 DataSet oder DataReader

Das ADO.NET-DataSet wurde explizit für den Datenzugri� unabhängig von Datenquellen entworfen. Aus die-sem Grund kann es mit mehreren, unterschiedlichen Datenquellen, mit XML-Daten oder zum Verwalten vonlokalen Anwendungsdaten verwendet werden. Das DataSet enthält eine Au�istung von einem oder mehrerenDataTable-Objekten, die aus Datenzeilen und -spalten sowie aus Primärschlüsseln, Fremdschlüsseln, Einschrän-kungen und Beziehungsinformationen über die Daten in den DataTable-Objekten besteht.

Wenn Sie sich zwischen einem DataReader oder einem DataSet für die Anwendung entscheiden möchten, müssenSie den für die Anwendung erforderlichen Funktionalitätstyp berücksichtigen. Verwenden Sie für folgende Zweckeein DataSet:

• Lokales Zwischenspeichern von Daten in Ihrer Anwendung, um sie bearbeiten zu können. Wenn Sie nurdie Ergebnisse einer Abfrage anzeigen müssen, ist der DataReader die bessere Alternative.

65 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 66: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

5.3 DataSet oder DataReader

Abbildung 5.4: Datenbankzugri� - Performanz

• Verschieben von Daten zwischen Ebenen oder von einem XML-Webdienst.

• Dynamisches Interagieren mit Daten, wie Datenbindung an ein Windows Forms-Steuerelement, oder Kom-binieren von und Erstellen einer Beziehung zwischen Daten aus mehreren Quellen.

• Ausführen umfangreicher Datenverarbeitungsschritte ohne eine o�ene Verbindung zur Datenquelle. Da-durch wird die Verbindung zur Nutzung durch andere Clients freigegeben.

Wenn Sie die von DataSet bereitgestellten Funktionen nicht benötigen, können Sie die Leistung Ihrer Anwen-dung verbessern, indem Sie mithilfe des DataReader die Daten als schreibgeschützte Vorwärtsdaten zurück-geben. Obwohl der DataAdapter den DataReader zum Füllen des DataSet-Inhalts verwendet (siehe Au�ülleneines DataSets mit einem DataAdapter-Objekt), können Sie die Leistung erhöhen, wenn Sie den DataReaderverwenden. Dies ist möglich, weil Sie den Arbeitsspeicher, der für das DataSet erforderlich ist, nicht benötigenund die Verarbeitung, die zum Erstellen und Füllen des DataSet-Inhalts nötig ist, über�üssig ist.

Neben den vorgestellten DB-Zugri�smöglichkeiten bietet das .Net Framework mit dem Entity Framework (EF)einen in VS gut integrierten OR-Mapper. Eine gut verständliche Einführung �nden Sie unter http://msdn.

microsoft.com/de-de/library/bb399567.aspx. Wie bei den Tests gibt es auch hier eine verbreitete undin Java verfügbare Alternative: Hibernate, bzw. für .Net NHibernate. Neben dem EF gibt es noch weitereKlassenbibliotheken für den Datenbankzugri�. Einen guten Vergleich liefert folgender Artikel: Daten im (Zu-)Gri� mit .NET: DataReader, DataSet oder ORM?. Aus diesem Artikel stammt auch Abbildung 5.4, die diePerformance der diskutierten DB-Zugri�smöglichkeiten vergleicht.

66 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 67: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

5.4 P4* - Datenbanken und Addins

5.4 P4* - Datenbanken und Addins

Abgabetermin: 01.05.2011

Aufgabe P4.1Erstellen Sie eine SQL-Datenbank für Ihren Audio-Player. Wählen Sie die DB-Struktur so, dass auch Videoda-teien einfach hinzugefügt werden können. Normalisieren Sie die Datenbank.

Aufgabe P4.2Fügen Sie als Persistierung zur Serialisierung die erstellte SQL-Datenbank hinzu.

Aufgabe P4.3Erstellen Sie eine einfache Consolen-Applikation, die . . .

(a) Mit einem DataReader Daten ausliest, die Sie auf der Console ausgeben.

(b) Mit einem DataSet Daten ausliest und Änderungen in die DB schreibt.

(c) Verwenden Sie zum Schreiben von Daten Kommandparameter. Warum sind parametrisierte Befehle sinn-voll?

(d) Verwenden Sie einen DataAdapter sinnvoll.

(e) Schreiben Sie 1000 Datensätze in eine Tabelle.

(f) Implementieren Sie einen einfachen Performanztest, der beim Lesen von 1000 Datensätzen DataReadermit DataSet vergleicht.

Aufgabe P4.4Lesen Sie Daten aus einer Datenbank aus und schreiben Sie sie in eine neue Excel-Datei.

Aufgabe P4.5Erstellen Sie ein Excel-Addin, bei dem Sie einen SQL-Servernamen eingeben können, daraufhin zeigen Sie eineCombo-Box mit allen Datenbanken an und bei Auswahl einer DB eine ComboBox mit den darin enthaltenenTabellen. Ein Click auf einen �Import-Button� importiert diese Daten in das aktuelle Tabellenblatt.

67 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 68: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6 Windows Presentation Foundation

Lernziele:

• Wesentliche Controls zur Ober�ächengestaltung bei Desktop-Anwendungen in .Net kennen lernen undzielgerichtet einsetzen können

• Prinzip der Datenbindung in .Net am Beispiel von WPF-Controls beschreiben und anwenden können

• Architektur für gröÿere Projekte mit ihren Vor- und Nachteilen kennen und mit Hilfe von Beispielenerläutern können

Windows Presentation Foundation (WPF) wird zur Ober�ächenentwicklung auf dem Desktop verwendet. Ur-sprünglich wurde hierfür die Technologie WinForm eingesetzt. Seit 2010 entwickelt Microsoft jedoch WinFormnicht mehr weiter und sieht die Zukunft der Desktop-Entwicklung in WPF. WPF ist eine Klassenbibliothek,die seit .Net 3.0 im Jahr 2006 zum .Net Framework gehört. WPF nutzt eine deklarative Programmierspra-che (XAML), zur Beschreibung von Ober�ächen. Objektmodell und XAML können 1:1 aufeinander abgebildetwerden, so dass Entwickler sich entscheiden können, welchen Teil sie in C# und welchen Teil sie in XAML imple-mentieren. Das einfache Zusammenklicken von Ober�ächen und das Anpassen in XAML fügt sich gut zueinander� in WinForm sollte man den von VisualStudio erzeugten Code besser nicht anfassen. Die Gra�kausgabe istvektorbasiert, damit ist ein Zoomen verlustfrei möglich.

6.1 XAML

XAML ist eine deklarative Beschreibungssprache für Ober�ächen auf der Basis von XML. XAML steht fürExtensible Application Markup Language. Sie wird auch zur De�nition von Work�ows in Windows Work�owfoundation (WF) verwendet � auf die wir in dieser Veranstaltung nicht eingehen. Dieser deklarative Ansatz wirdauch von anderen verfolgt, wie folgende Auswahl zeigt:

• XUL: XML User Interface Language (Mozilla Projekt)

• UIML: OASIS User Interface Markup Language

• MXML von Adobe

Zur Laufzeit wird aus XAML die entsprechenden Objekte der .Net-Klassen erzeugt.

6.2 Architektur von Windows Presentation Foundation und das Programmiermodell

Wenn Sie ein neues WPF-Projekt anlegen, werden drei wichtige Referenzen eingebunden: PresentationFrame-work.dll, PresentationCore.dll, mscorlib.dll und WindowsBase.dll. Diese Bibliotheken sind die Hauptkompo-nenten von WPF. Sie können sich einfach einen guten Überblick über die Architktur verscha�en, in dem Siefolgende Schritte ausführen:

1. Erzeugen Sie ein neues WPF-Projekt.

2. Erzeugen Sie ein benutzerde�niertes Abhängigkeitsdiagramm. Wählen Sie Assemblies, Typen und Externaus.

3. Erkunden Sie die einzelnen Komponenten im Diagramm.

4. Erkunden Sie die Assemblies im ObjectExplorer.

68

Page 69: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

Abbildung 6.1: WPF Architektur

Abbildung 6.1 zeigt die Hauptkomponenten von WPF. �PresentationFramework� enthält die top-level Typen,z.B. auch die weiter unten betrachtete Basisklasse �FrameworkElement�. �PresentationCore� enthält Basistypenwie �UIElement�. �WindowsBase� enthält das �DependencyObject�. Die Komponente �milcore� kommuniziertdirekt mit DirectX und ist für das Rendern zuständig. Microsoft hat sich dafür entschieden, �milcore� zurbesseren Performance als unmanaged Code zu entwickeln, sie läuft also auÿerhalb der CLR. Abbildung 6.2 zeigtdie Vererbungshierarchie von �MainWindow�. Dies können Sie einfach erzeugen, in dem Sie im Projectexplorereinen Rechtsklick auf die cs-Datei machen, sich das Klassendiagramm erzeugen lassen und nun iterativ mitRechtsklick im Diagramm die Basisklasse anzeigen lassen.

Abbildung 6.3 zeigt eine Übersicht wesentlicher GUI-Elemente, die von �FrameworkElement� erben.

Hat man ein Projekt aufgrund der WPF-Vorlage erstellt, so ist dies direkt lau�ähig, es wird ein leeres Fenster an-gezeigt. Schaut man in den Code rein, so vermisst man ein ähnliches Konstrukt wie bei der Console-Anwendung:Im Code �ndet sich keine Main-Funktion. Ö�nen Sie im Datei-Explorer das beim Build erzeugte Verzeichnis�obj�, dort �nden Sie eine Datei �App� und darin enthalten ist eine Main-Funktion wie unten wiedergebeben.

Listing 6.1: WPF Main

[System.STAThreadAttribute()]

[System.Diagnostics.DebuggerNonUserCodeAttribute()]

public static void Main() {

WpfApplication1.App app = new WpfApplication1.App();

app.InitializeComponent();

app.Run();

In der Datei �App.xml� de�nieren Sie mit �StartupUri�, welche XAML-Datei zuerst geladen wird.

6.3 Steuerelemente und Layout

Üblicherweise enthält das Hauptfenster einen Layoutcontainer, also ein Grid, ein Stack- Dock- oder Wrap-Paneloder ein Canvas und darin enthaltenen Kindobjekten. In der Toolbox �nden Sie eine Reihe von nützlichenControls, aus denen Sie Ihre Ober�äche zusammen setzen können. Dies können Sie per Drag&Drop, in XAMLoder auch in C# tun. Ziehen Sie beispielsweise ein Button-Control auf MainWindow, so wird folgender XAML-Code erzeugt:

<Grid Name="mainGrid" >

<Button Content="Button" Height="41" HorizontalAlignment="Left" Margin="74,70,0,0" Name="button1"

VerticalAlignment="Top" Width="141" Click="button1_Click" />

</Grid>

69 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 70: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

Abbildung 6.2: WPF Vererbungshierarchie

Folgender C#-Code erzeugt analog einen Button und fügt ihn in das Grid ein:

Button b = new Button();

b.Name = "button2";

b.Content = "Button 2";

b.Height = 41;

b.Width = 141;

this.mainGrid.Children.Add(b);

Beachten Sie, dass für �Content� ein String genutzt wurde, Sie hätten auch andere Elemente wählen können,denn ein Button kann auch andere Controls, Bilder etc. enthalten.

Den Aufbau existierender Applikationen können Sie gut mit dem freien Werkzeug Snoop erkunden.

Barrierefreiheit ist nicht nur im Web wichtig, sondern bei jeder Softwareentwicklung. Vergeben Sie daher füralle Controls einen sinnvollen Titel und eine sinnvolle Tab-Folge.

6.3.1 Container

Container werden für das Layout, also die grobe Aufteilung der einzelnen Fenster verwendet. Im Prinzip �ndenSie hier ähnliche Positionierungsmethoden wieder, die sie in EWA bei CSS kennen gelernt haben:

• Canvas (absolute Positionierung): Bei Canvas kann immer nur eine Positionsart de�niert werden, �Top�oder �Left� oder �Bottom� oder �Right�.

• StackPanel (Blockelemente untereinander): Das Stackpanel funktioniert wie der Name schon sagt, wie einStapel, die Elemente werden untereinander angeordnet, dabei können Ränder de�niert werden. Marginwird anders als bei CSS in der Reihenfolge links, oben, rechts, unten angegeben. Wird keine Breite de�niert,dann gehen die Elemente über die gesamte Breite des StackPanels.

• DockPanel: Die Controls können an den Rändern angedockt werden. Dies können mehrere hintereinandersein. Auch, wenn eine Breite angegeben wird, passt kein weiteres Element daneben � hierfür wird einWrapPanel benötigt. Falls die Standardeinstellung �LastChildFill� gewählt wird, dann füllt das letzteKindeelement den verbleibenden Platz.

• WrapPanel (�oat): Die Elemente können nebeneinander �oaten. Ist keine Breite de�niert, so nehmen siedie Gröÿe ein, die der Inhalt braucht.

70 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 71: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

Abbildung 6.3: FrameworkElement, Quelle: WinfWiki

• Grid (Tabelle): Zunächst müssen die Zeilen und Spalten de�niert werden (es reicht auch eines). Danachwerden diese gefüllt. Mit GridSplitter kann die Benutzerin die Gröÿe selbst anpassen.

Folgendes Code-Beispiel verbindet alle Layouts und Abbildung 6.4 zeigt das anfängliche Aussehen.

Listing 6.2: Layout Controls

�<Window x:Class="WpfLayoutContainer.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Layout Container" Height="250" Width="900">

<Grid>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="200" />

<ColumnDefinition Width="200" />

<ColumnDefinition Width="200" />

<ColumnDefinition Width="200" />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="30" />

<RowDefinition Height="Auto"/>

</Grid.RowDefinitions>

<Rectangle Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" Fill="Silver" />

<GridSplitter ResizeDirection="Rows" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4" Background="Gray"

Width="800" Height="5" HorizontalAlignment="Left" VerticalAlignment="Top" />

<Canvas Grid.Row="1" Grid.Column="0">

<Label Content="top absolut positioniert mit Canvas" Canvas.Top="20" />

<Button Name="DisableMouseEnterBtn" Content="deactivate mouse over" Canvas.Bottom="5" Click="

DisableMouseEnterBtn_Click" />

</Canvas>

<GridSplitter Grid.Row="1" Grid.Column="0" Background="Red" Width="5"/>

<StackPanel Grid.Row="1" Grid.Column="1">

<Label Grid.Row="1" Content="erstes Label" Margin="5" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="zweites Label" Margin="30" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="drittes Label (kleiner)" Margin="5" Width="150" BorderBrush="#FF190000" BorderThickness

="2"/>

</StackPanel>

<GridSplitter Grid.Row="1" Grid.Column="1" Background="Red" Width="5"/>

<DockPanel Grid.Row="1" Grid.Column="2">

<Label Content="dock Top" DockPanel.Dock="Top" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="dock Bottom" DockPanel.Dock="Bottom" BorderBrush="#FF190000" BorderThickness="2" />

71 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 72: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

<Label Content="dock Top 2" DockPanel.Dock="Top" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="dock Left" DockPanel.Dock="Left" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="last child fill" DockPanel.Dock="Top" BorderBrush="#FF190000" BorderThickness="2" />

</DockPanel>

<GridSplitter Grid.Row="1" Grid.Column="2" Background="Red" Width="5"/>

<WrapPanel Name="WrapPanelWithEvents" Grid.Row="1" Grid.Column="3" MouseEnter="

WrapPanelWithEvents_MouseEnter" ButtonBase.Click="WrapPanelWithEvents_Click">

<Label Content="label 1" BorderBrush="#FF190000" BorderThickness="2" />

<Label Name="lblWithMouseEnter" Content="mouse enter:" BorderBrush="#FF190000" BorderThickness="2"

MouseEnter="lblWithMouseEnter_MouseEnter" />

<Label Content="label 3 mit Breite" Width="200" BorderBrush="#FF190000" BorderThickness="2" />

<Label Content="label 3..." BorderBrush="#FF190000" BorderThickness="2" />

<Button Name="btnClick" Content="click" Click="btnClick_Click" />

</WrapPanel>

</Grid>

</Window>

Abbildung 6.4: WPF Layout Beispiel

Frage 6.1Nennen Sie jeweils ein Beispiel, bei dem Sie ein DockPannel bzw. ein StackPannel bzw. ein Grid sinnvoll einsetzenwürden.

Wenn alle Controls erzeugt wurden, erhält man einen Baum. Es wird zwischen dem logischen und dem visuellenBaum unterschieden.

6.3.2 Logischer Baum

Der logische Elementbaum enthält alle von Ihnen eingefügten Steuerelemente. Mit dem Einfügen eines Controlskönnen indirekt weitere Elemente eingefügt werden.

6.3.3 Visueller Baum

Der visuelle Baum enthält zusätliche Elemente wie Border, ContentPresenter oder TextBlock.

Der folgende Code nutzt die in .Net vorhandenen Klassen �VisualTreeHelper� und �LogicalTreeHelper� zumAnzeigen der Elementbäume des unteren Panels.

Listing 6.3: Erzeugen des visuellen und logischen Elementbaumes

private void fillTrees()

{

TreeViewItem rootVi = new TreeViewItem();

rootVi.Header = "Visueller Elementbaum des unteren Panels:";

viTree.Items.Add(rootVi);

72 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 73: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

showVisualTree(rootVi, demoPanel);

TreeViewItem rootLo = new TreeViewItem();

rootLo.Header = "Logischer Elementbaum des unteren Panels:";

loTree.Items.Add(rootLo);

showLogicalTree(rootLo, demoPanel);

}

private void showVisualTree(TreeViewItem parentItem, DependencyObject parentObj)

{

TreeViewItem item = new TreeViewItem();

item.Header = parentObj.GetType().ToString();

parentItem.Items.Add(item);

for(int i=0; i< VisualTreeHelper.GetChildrenCount(parentObj); i++)

showVisualTree(item, VisualTreeHelper.GetChild(parentObj, i));

}

private void showLogicalTree(TreeViewItem parentItem, DependencyObject parentObj)

{

TreeViewItem item = new TreeViewItem();

item.Header = parentObj.GetType().ToString();

parentItem.Items.Add(item);

foreach (object obj in LogicalTreeHelper.GetChildren(parentObj))

{

DependencyObject dep = obj as DependencyObject;

if (dep != null)

showLogicalTree(item, dep);

}

}

6.3.4 Routed Events

Erinnern Sie sich noch daran, wie Ecma-Skript Ereignisse von ineinander geschachtelten Elementen verarbeitet?Was passiert beispielsweise bei folgendem Code, wenn Sie auf P bzw. DIV klicken?

<div onclick="this.style.background='red';">DIV

<p onclick="this.style.background='blue';>P</p>

</div>

Und, was passiert bei folgendem Code:

Listing 6.4: JS Ereignisweiterleitung

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>

<title>Ereignisweiterleitung</title>

<script type="text/javascript">

function bubble(id)

{

window.alert( "bubble " + id );

}

function capture(id)

{

window.alert( "capture " + id );

}

function ini()

{

document.getElementById("table1").addEventListener( "click", function(){bubble("table1")}, false );

// - use capture = false

document.getElementById("table1").addEventListener( "click", function(){capture("table1")}, true );

document.getElementById("tr1").addEventListener( "click", function(){bubble("tr1")}, false ); // -

use capture = false

document.getElementById("tr1").addEventListener( "click", function(){capture("tr1")}, true );

document.getElementById("td1").addEventListener( "click", function(){bubble("td1")}, false ); // -

use capture = false

document.getElementById("td1").addEventListener( "click", function(){capture("td1")}, true );

}

</script>

73 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 74: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

</head>

<body onload="ini();">

<table id="table1">

<tr id="tr1"><td id="td1">11111111111111</td><td>22222222</td></tr>

<tr><td>3</td><td>4</td></tr>

</table>

<p>Hello World!</p>

</body>

</html>

Hier werden die Ereignisse zunächst von auÿen nach innen und dann wieder von innen nach auÿen geleitet.

Listing 6.5: Layout Controls - EventHandler

20 public partial class MainWindow : Window

21 {

22 public MainWindow()

23 {

24 InitializeComponent();

25 AssingMyRoutedEvent();

26 }

2728 private void lblWithMouseEnter_MouseEnter(object sender, MouseEventArgs e)

29 {

30 Label tmp = sender as Label;

31 if (tmp != null)

32 tmp.Content = tmp.Content + " <> ";

33 e.Handled = false;

3435 }

3637 private void WrapPanelWithEvents_MouseEnter(object sender, MouseEventArgs e)

38 {

39 MessageBox.Show("wrap panel, mouse enter"); //System.Diagnostics.Debug.WriteLine("wrap panel, mouse enter");

40 }

4142 private void DisableMouseEnterBtn_Click(object sender, RoutedEventArgs e)

43 {

44 lblWithMouseEnter.MouseEnter -= this.lblWithMouseEnter_MouseEnter;

45 lblWithMouseEnter.Content = "inactive";

46 WrapPanelWithEvents.MouseEnter -= this.WrapPanelWithEvents_MouseEnter;

47 }

4849 private void btnClick_Click(object sender, RoutedEventArgs e)

50 {

51 Button tmp = sender as Button;

52 if (tmp != null)

53 tmp.Content = tmp.Content + " <r> ";

54 }

5556 private void WrapPanelWithEvents_Click(object sender, RoutedEventArgs e)

57 {

58 MessageBox.Show("wrap panel button click");

Zunächst ist festzustellen, dass es in WPF zwei Arten von Events gibt. Ereignisse, wie �MouseEnter� entsprechenden bisher kennen gelernten Ereignissen. Von innen nach auÿen wird nach einem Handler gesucht. Ist diesergefunden, ist die Behandlung erledigt - es können natürlich meherere EventHandler für das gleiche Ereignisdesselben Controls registriert werden. In Listing 6.2 wird in den Zeilen 39 und 41 beispielsweise sowohl für einLabel als auch für den zugehörigen Container ein MouseEnter-Ereignishandler (Zeilen 27 und 36 in Listing 6.7)registriert. Erst durch Zeile 32, in der �Handled� auf �false� gesetzt wird, wird ein Weiterreichen erzwungen.Nun wird innerhalb des logischen Elementbaumes geschaut, welche Elternelemente ein EreignisHandler für�MouseEnter� registriert haben. Ob nun die Strategie, tunneling oder bubbling verwendet wird, liegt an derArt des Ereignisses, Preview-Ereignisse, wie PreviewMouseDown werden getunnelt, während MouseDown undähnliche Ereignisse mit bubbling den Baum durchlaufen.

Möchten wir nun auf ein Click-Ereignis auf den im Panel enthaltenen Button reagieren, so haben wir bei dermomentan angezeigten �MessageBox� das Problem, dass es nie zum Click-Ereignis kommt. Daher gibt es einenzusätzlichen Button, der die EventHandler für MouseEnter wieder entfernt (siehe Zeile 41). Alternativ hättenwir die Ausgabe auch einfach auf das Debug-Output-Fenster umleiten können (siehe auskommentierter Codein Zeile 38). In Listing 6.7 werden EventHandler in den Zeilen 39 und 44 von Listing 6.2 für ein �Button.Click�

74 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 75: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

registriert. Die generierten EventHandler enthalten nun einen Parameter vom Typ �RoutedEventArgs� (sieheZeile 48 in Listing 6.7). Wie der Name vermuten lässt, werden diese Events per default weitergeleitet. Hiermüsste ich also Handled explizit auf True setzen, um die Weiterleitung zu unterdrücken.

Schauen wir uns die Basis für beide Events an (wieder in der generierten Datei MainWindow.g.cs zu �nden,dann sehen wir folgende De�nitionen für das WrapPanel und mit F12 erhalten wir die zugehörigen in .NetDe�nitionen:

Listing 6.6: WPF Main

1 //MouseEnter

2 this.WrapPanelWithEvents.MouseEnter += new System.Windows.Input.MouseEventHandler(this.

WrapPanelWithEvents_MouseEnter);

3 //.Net De�nitions

4 namespace System.Windows{ public class UIElement ... {... {public event MouseEventHandler MouseEnter;...}}

5 namespace System.Windows.Input{public delegate void MouseEventHandler(object sender, MouseEventArgs e);}

67 //Button.Click

8 this.WrapPanelWithEvents.AddHandler(System.Windows.Controls.Primitives.ButtonBase.ClickEvent, new System.Windows.

RoutedEventHandler(this.WrapPanelWithEvents_Click));

910 //.Net De�nitions

11 namespace System.Windows{ public class UIElement ... {... {public void AddHandler(RoutedEvent routedEvent,

Delegate handler);...}}

12 namespace System.Windows.Controls.Primitives{public abstract class ButtonBase{...public static readonly

RoutedEvent ClickEvent;...}}

13 namespace System.Windows{ public delegate void RoutedEventHandler(object sender, RoutedEventArgs e);}

Sie sehen, dass in Zeile 2 ein �MouseEventHandler�, also ein �event� im eigentlichen Sinne übergeben wird (Zeile4). Während bei �Button.Click� ein �ClickEvent�, also ein �RoutedEvent� übergeben wird.

Wenn Sie sich beim Debuggen bei Zeile 12 das RoutedEvent ansehen, können Sie die verwendete Strategie sehen:�Bubble�. Diese kann bei RoutedEvents generell de�niert werden. Sie können sich analog zu Events auch eigeneRoutedEvents de�nieren. Den prinzipiellen Aufbau zeigt folgendes Listing, das sich an Listing 6.7 anschlieÿt.

Listing 6.7: Layout Controls - EventHandler

RoutedEventArgs mye = new RoutedEventArgs(MyRoutedEvent, this);

WrapPanelWithEvents.RaiseEvent(mye);

}

public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent("MyEvent",

RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(Button));

private void AssingMyRoutedEvent()

{

this.WrapPanelWithEvents.AddHandler(MainWindow.MyRoutedEvent, new System.Windows.RoutedEventHandler(

this.WrapPanelWithEvents_MyRoutedEvent));

}

private void WrapPanelWithEvents_MyRoutedEvent(object sender, RoutedEventArgs e)

{

MessageBox.Show("wrap panel my routed event");

}

Frage 6.2Werden �Routed Events� entlang des visuellen oder entlang des logischen Baums weitergeleitet?

Frage 6.3Nehmen Sie zu folgender Aussage Stellung: �Routed Events� werden in .Net zunächst von innen nach auÿen(bubble) entlang des visuellen Baumes weitergereicht und anschlieÿend entlang des logischen Baumes von auÿennach innen (tunnel) durchgereicht.�

75 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 76: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.3 Steuerelemente und Layout

Klasse Beispiele

ApplicationCommands Close, Cut, Copy, Paste, Save, PrintNavigationCommands BrowseForward, BrowseBack, Zoom, SearchEditingCommands AlignXXX, MoveXXX, SelectXXXMediaCommands Play, Pause, NextTrack, IncreaseVolume, Record, StopComponentCommands MoveXXX, SelectXXX, ScrollXXX, ExtendSelectionXXX

Tabelle 6.1: WPF Commands, Quelle: Understanding Routed Events and Commands In WPF

6.3.5 Command

Commands bilden die Möglichkeit, die Ober�äche von der eigentlichen Applikationslogik zu entkoppeln. Dieüblichen Ober�ächenkommandos, wie Copy, Paste, Cut stellt .Net vorimplementiert zur Verfügung. Alles, wasman hierfür tun muss, ist den entsprechenden Command im XAML-Attribut Command anzugeben:

<MenuItem Command="Copy" />

Hier wird also keine zusätzliche Programmierung benötigt. Tabelle 6.1 listet einige vorhandene Commands auf.

Mit diesen vordefnierten Commands lässt sich ein Editor implementieren.

Frage 6.4Welche Controls unterstützen die Eigenschaft �Command�?

Auf selbst de�nierte Commands gehe ich in Abschnitt 6.6 auf Seite 80 ein.

76 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 77: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.4 Praktikum P5* - WPF Ober�äche

6.4 Praktikum P5* - WPF Ober�äche

Abgabetermin: 08.05.2011

Aufgabe P5.1Analysieren Sie mit Snoop eine WPF-Anwendung Ihrer Wahl. Notieren Sie den groben Aufbau der GUI.

Aufgabe P5.2Wodurch unterscheiden sich der visuelle und der logische Element-Baum einer WPF-Ober�äche?

Abbildung 6.5: Layoutaufgabe

Aufgabe P5.3Setzen Sie das in Abbildung 6.5 gezeigte Layout um.

(a) Fertigen zunächst eine Skizze an.

(b) Implementieren Sie das Layout in WPF.

(c) Suchen Sie nach �SpellingError� und fügen Sie eine Korrekturhilfe hinzu.

(d) Fügen Sie exemplarisch die Commands Cut, Copy, und Paste hinzu.

Aufgabe P5.4Fügen Sie Ihrem Audio-Player eine geeignete WPF-Ober�äche hinzu.

77 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 78: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.5 Datenbindung

6.5 Datenbindung

Bei der Datenbindung in WPF geht es darum, Ober�ächenelemente (Controls) zum Anzeigen und Ändern vonDaten zu verwenden. Abbildung 6.6 zeigt den prinzipiellen Aufbau der Datenbindung.

Abbildung 6.6: WPF Datenbindung, Prinzip

Auf der GUI-Seite wird dabei ein DependencyProperty benötigt und auf der Datenseite eine Klasse, die dasInterface INotifyPropertyChanged implementiert.

6.5.1 DependencyProperty

Eine DependencyProperty wurde in WPF zur einfacheren Umsetzung von Datenbindung und zur einfachenUmsetzung einheitlicher Styles eingeführt. Es handelt sich hier um eine Property, die syntaktisch genausoangesprochen wird, die jedoch über entsprechende Setter- und Getter-Methoden verfügt, die Änderungseventsauslösen können. Für eine Textbox ist dies beispielsweise:

//Der Bezeichner für die System.Windows.Controls.TextBox.Text−Abhängigkeitseigenschaft.public static readonly DependencyProperty TextProperty;

�DependencyProperty� ist immer als �static readonly� zu deklarieren.

Frage 6.5Wodurch unterscheidet sich ein �DependencyProperty� von einem �normalen Property�?

6.5.2 INotifyPropertyChanged

Die Schnittstellende�nition für �INotifyPropertyChanged� be�ndet sich im Namensraum �Sys-tem.ComponentModel� und beinhaltet folgende De�nition:

//Benachrichtigt Clients , dass ein Eigenschaftswert geändert wurde.

public interface INotifyPropertyChanged

{

//Tritt ein , wenn sich ein Eigenschaftswert ändert.

event PropertyChangedEventHandler PropertyChanged;

}

Wenn wir für eine einfache Klasse �Student� diese Schnittstelle implementieren möchten, so müssen wir das�event� einfügen. Wenn wir noch das �event� bei Änderungen auslösen, dann kann die GUI oder andere Listenerdiese Änderungen auch mitbekommen. Den zugehörigen Code zeigt Listing 6.8

Listing 6.8: INotifyPropertyChanged

public string Name

{

get { return _name; }

set { _name = value; OnPropertyChanged("Name"); }

}

public string Mark

{

get { return _mark; }

set { _mark = value; System.Diagnostics.Debug.WriteLine("mark changed to " + _mark); OnPropertyChanged(

"Mark"); }

}

78 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 79: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.5 Datenbindung

protected virtual void OnPropertyChanged(string propName)

{

if (PropertyChanged != null) //at least one is interested

{

PropertyChanged(this, new PropertyChangedEventArgs(propName));

}

}

}

}

Die Klasse Student ist als Singleton implementiert, um später zu zeigen, die Änderungen können von verschie-dener Seite geändert werden und die entsprechenden Bindungen spiegeln diese Änderungen wider.

Frage 6.6Wäre es auch möglich, in der Schnittstelle �INotifyPropertyChanged� anstelle von �event� das Schlüsselwort�delegate� zu verwenden?

6.5.3 Binding

Jetzt können wir recht einfach eine Datenbindung herstellen, wie Listing 6.9 zeigt.

Listing 6.9: INotifyPropertyChanged

31 _student = Student.GetInstance();

32 lblName.Content = _student.Name;

33 Binding bindingMark = new Binding("Mark"); //path = name of property

34 bindingMark.Source = _student;

35 bindingMark.Mode = BindingMode.TwoWay;

36 bindingMark.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; //with every key press

37 txtMark.SetBinding(TextBox.TextProperty, bindingMark);

3839 }

4041 private void btn1_Click(object sender, RoutedEventArgs e)

42 {

43 _student.Mark = "1.0";

44 }

4546 private void btn2_Click(object sender, RoutedEventArgs e)

47 {

48 _student.Mark = "2.0";

49 }

5051 private void btnOpenWindow_Click(object sender, RoutedEventArgs e)

52 {

53 DataContextExample w = new DataContextExample();

54 w.Show();

55 }

56 }

57 }

In Zeile 33 wird dem Konstruktor der Klasse �Binding� als Pfad zur Bindungsquelle der Propertyname über-geben, an den das Control gebunden werden soll. Zeile 34 legt die eigentliche Datenquelle fest, die Instanz�_student�. Zeile 35 de�niert, die Bindung in beide Richtungen, Änderungen von den Daten zum GUI-Controlund umgekehrt werden mitgeteilt. Zeile 36 sagt schlieÿlich, dass jede Änderung im Control direkt kommuniziertwird, in diesem Fall also jede Tastatureingabe. Bemerkung: Das Projekt kann kompilieren und im starten, auchwenn Sie in Zeile 33 vielleicht einen Tippfehler gemacht haben und die Property so nicht existiert. Dies erzeugtbeim normalen Compilieren nicht einmal eine Warnung. Erst zur Laufzeit, also z.B. beim Starten des Projektsim Debug-Modus, erhalten Sie folgende Fehlermeldung im Output-Fenster:

79 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 80: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.5 Datenbindung

System.Windows.Data Error: 40 : BindingExpression path error: 'Markx' property not found on 'object' ''Student' (

HashCode=29677729)'. BindingExpression:Path=Markx; DataItem='Student' (HashCode=29677729); target element is

'TextBox' (Name='txtMark'); target property is 'Text' (type 'String')

Die Zeilennummer bezieht sich dabei leider nicht auf Ihren Code.

Abbildung 6.7 zeigt den prinzipiellen Aufbau der Datenbindung unter Verwendung der eingeführten Schnitt-stellen und Klassen. Die Methode �SetBinding� stellt die Verbindung her.

Abbildung 6.7: WPF Datenbindung, Prinzip aus Codesicht, Quelle: Christian Mosers WPF Tutorial.net

Merke 9 (Datenbindung)Die Datenbindung erfordert

• ein Objekt (Quelle) einer Klasse, die INotifyPropertyChanged implementiert

• ein Objekt (Control) mit einem DependencyProperty

• ein Bindungsobjekt (Binding)

• den Aufruf der Methode SetBinding des Controls

Frage 6.7Wann wird bei einer Datenbindung wo ein Listener beim Event von �INotifyPropertyChanged� registriert bzw.deregistriert?

Frage 6.8Bleibt einer Datenbindung zu einer Datenbank die Verbindung erhalten oder nicht?

6.5.4 DataContext

Neben der bisher aufgeführten Möglichkeit der Datenbindung auf Code-Ebene, kann eine Datenbindung auchin XAML auf Basis eines DataContext erfolgen:

Listing 6.10: DataContext

_student = Student.GetInstance();

this.DataContext = _student;

80 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 81: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.6 MVVM

Die De�nition der Datenbindung erfolgt in XAML:

Listing 6.11: Binding in XAML mit DataContext<TextBox Name="txtMark" Text="{Binding Path=Mark, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

Frage 6.9Was sind die wesentlichen Eigenschaften und Methoden von �DataContext� und wie unterscheidet sich dieserAnsatz von einer Datenbindung auf Basis von �INotifyPropertyChanged�?

6.6 MVVM

Abbildung 6.8: MVVM, Quelle: Nikhil Kothari

CodeBehind ist für Unit-Tests nicht erreichbar, kann also nicht automatisch getestet werden. Dieser Code istauch neu zu programmieren, wenn Sie beispielsweise die Applikation auch für mobile Endgeräte oder das Webverfügbar machen möchten. Das zugehörige Pattern für WPF heiÿt �Model View View Model� und wird mitMVVM abgekürzt. Die Basis dieses Patterns sind �Datenbindung� und �Command�.

Als Denkhilfe können Sie folgende Vereinfachung verwenden: MVVM ist eine auf WPF angepasste Version vonMVC. Der Controller wird so verpackt, dass WPF mit DataBinding und Commands auf CodeBehind verzichtenkann.

Betrachten wir hierzu ein einfaches Beispiel. Die Funktionalität der Applikation ist zu Demonstrationszweckenstark reduziert. Ein Model verwaltet seine Daten. Über die in Listing 6.12 de�nierte Funktionen könnten auchValidierungs- oder DB-Probleme integriert werden.

Listing 6.12: Einfaches Model für MVVMpublic interface IStudentProvider

{

void Add(Student student);

IEnumerable<Student> GetStudents();

int Count { get; }

}

public class MockStudentProvider : IStudentProvider

{

private List<Student> _students;

public MockStudentProvider()

{

_students = new List<Student>();

_students.Add(new Student("Asterix", "1.0"));

_students.Add(new Student("Obelix", "3.0"));

}

public void Add(Student student)

{

_students.Add(student);

}

81 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 82: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.6 MVVM

public IEnumerable<Student> GetStudents()

{

return _students;

}

public int Count

{

get { return _students.Count; }

}

}

Bei MVVM interagiert die View nur mit dem ViewModel und nicht mit dem Model selbst. Ein ViewModel stelltdie Daten so zur Verfügung, wie in Abschnitt 6.5.4 auf Seite 79 eingeführt und um entsprechendes Verhalten(Commands) erweitert. Dies zeigt Listing 6.13.

Listing 6.13: Einfaches ViewModel für MVVMclass AddStudentCommand : ICommand

{

StudentViewModel _viewModel;

public AddStudentCommand(StudentViewModel viewModel)

{

_viewModel = viewModel;

}

public bool CanExecute(object parameter)

{

return true; //here this is no problem, might be di�erent in other cases

}

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)

{

//parameter is null in our context

_viewModel.Add();

}

}

public class BaseViewModel: INotifyPropertyChanged

{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName)

{

if (PropertyChanged != null)

{

PropertyChanged(this,

new PropertyChangedEventArgs(propertyName));

}

}

}

public class StudentViewModel : BaseViewModel

{

private IStudentProvider _model;

private AddStudentCommand _addCmd;

private Student _selectedStudent;

private string _nameToAdd;

public StudentViewModel(IStudentProvider model)

{

_model = model;

_addCmd = new AddStudentCommand(this);

}

public int Count

{

get { return _model.Count; }

}

public ICommand AddCmd

{

get { return _addCmd; }

}

public void Add()

{

_model.Add(new Student(_nameToAdd));

82 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 83: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.6 MVVM

UpdateStudents();

}

public string NameToAdd

{

set { _nameToAdd = value; }

get { return _nameToAdd; }

}

public List<Student> Students

{

get { return _model.GetStudents().ToList<Student>(); }

}

public Student SelectedStudent

{

get { return _selectedStudent; }

set { _selectedStudent = value; OnPropertyChanged("SelectedStudent"); }

}

private void UpdateStudents()

{

OnPropertyChanged("Students");

OnPropertyChanged("Count");

}

}

Es werden zunächst auf Basis von �ICommand� mögliche Commands de�niert. Die Basisklasse bereitet dasDurchreichen von Änderungen an evtl. gebundene Controls vor. Die Implementation selbst ist manchmal etwashakelig, da ein Debuggen der Datenbindung schwierig ist.

Die View nutzt Datenbindung und Command des ViewModels. Lediglich die Eigenschaft �DataContext� ist aufdas ViewModel zu setzen.

Listing 6.14: Einfache View für MVVM<ListView SelectedItem="{Binding Path=SelectedStudent,Mode=OneWayToSource}" ItemsSource="{Binding Path=

Students, Mode=OneWay}" >

<ListView.View>

<GridView>

<GridViewColumn Header="Name"

DisplayMemberBinding="{Binding Path=Name}" />

<GridViewColumn Header="Mark"

DisplayMemberBinding="{Binding Path=Mark}" />

</GridView>

</ListView.View>

</ListView>

<TextBox Name="vwName" Text="{Binding Path=NameToAdd,Mode=OneWayToSource}" />

<Button Name="vwAdd" Content="Add" Command="{Binding Path=AddCmd}"/>

<Label Content="Selected Student name:"/>

<Label Content="{Binding Path=SelectedStudent.Name}" />

Frage 6.10Beschreiben Sie die Aufgaben der Komponenten und die Kommunikation zwischen den einzelnen Komponenteneiner MVVM-Applikation in eigenen Worten.

Frage 6.11Diskutieren Sie Unterschiede und Gemeinsamkeiten des Controllers im MVC-Pattern und des View-Models imMVVM-Pattern.

83 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 84: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.6 MVVM

Frage 6.12Vergleichen Sie MVVM mit dem von Fowler eingeführten Pattern �Presentation Model� ein. Er beschreibt es indiesem Artikel: http://martinfowler.com/eaaDev/PresentationModel.html .

Frage 6.13Fügen Sie die Möglichkeiten �Edit� und �Delete� in die MVVM-Applikation hinzu.

Frage 6.14Beim Click auf einen Button soll ein OpenFile-Dialog geö�net werden und die Benutzerin kann über diesenDialog eine oder mehrere Dateien auswählen. Wie setzen Sie dies innerhalb einer MVVP-Applikation um?

84 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 85: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.7 Praktikum P6* - WPF Datenbindung, Audio-Player

6.7 Praktikum P6* - WPF Datenbindung, Audio-Player

Abgabetermin: 15.05.2011

Aufgabe P6.1Binden Sie ein einfaches Objekt an ein geeignetes Control, so dass Änderungen in beide Richtungen propagiertwerden.

Aufgabe P6.2Binden Sie ein DataGrid an eine Datenbank und ermöglichen Sie das Ändern und Hinzufügen von Daten.

Aufgabe P6.3Wenden Sie das MVVM-Pattern auf Ihre Applikation an.

Aufgabe P6.4Stellen Sie Ihren Audio-Player mit automatischen Tests und geeigneten Diagrammen fertig.

85 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011

Page 86: Skript: .Net Framework und C# - fbi.h-da.de · PDF file.Net Framework und C# SS 2011 Teil I Dr. Ute Blechschmidt-Trapp Darmstadt, Darmstadt, 4. April 2011

6.8 Links

6.8 Links

• Diplomarbeit Potenziale und Grenzen der Windows Presentation Foundation

• WPF documentation samples bietet viele Beispiele zu isolierten typischen Problemen.

• Understanding Routed Events and Commands In WPF

• Snoop Werkzeug, um den Aufbau einer WPF-Applikation zu untersuchen.

• Presentation Model Fowlers Darstellung des Patterns Presentation Model, auf das sich MVVM beruft.

• WPF Apps With The Model-View-ViewModel Design Pattern

• patterns & practices: Prism Framework für MVVM-Applikationen

• MVVM Light Toolkit Schlankes MVVM-Framework

86 c©Dr. Ute Blechschmidt-Trapp, .Net Framework und C# SS 2011