© Martin Korneffel, Stuttgart 2005 +++ email: trac@n.zgs.de +++ web: www.s-line.de/homepages/trac
Zum Testen von Software sowie zur Laufzeitanalyse liefert .NET Tools in der Klassenbibliothek unter System.Diagnostics.
|
Tools |
Kurzbeschreibung |
|---|---|
|
Debuggerattribute |
Dienen der detailierten Steuerung des Debug- Prozesses. So können bereits ausgetestete Methoden für den Einzelschrittmodus gesperrt oder spezielle Member können im Überwachungsfenster in der Wertespalte als Liste ausgegeben werden. |
|
Debug- Klasse |
Die Klasse ist nur im Debug- Build aktiv. Im Releasebuild wird die Methodenaufrufe der Klasse nicht in MSIL compiliert. Die Klasse stellt eine Schnittstelle zum Debuger da. Der Debugger kann über Debug gestartet, Breakpoints können ausgelöst und Meldungen an den Debugger übergeben werden. Desweiteren können Behauptungen über den aktuellen Ausführungszustand (Assert) aufgestellt und mit dem Ist- Zustand verglichen werden. |
|
Trace- Klasse |
Ausgabe von Meldungen im Debug, als auch im Release- Mode. |
|
Trace- Listener |
Sind Ausgabemedien für die von der Klasse Trace oder Debug generierten Meldungen |
|
TraceSource |
|
|
TraceSwitch, BooleanSwitch |
Sind Schalter, die über Konfigurationsdateien eingestellt, und in Bedingten Debug- oder Trace- Methoden ausgewertet werden können. |
Die Instrumentierung einer Anwendung mit Debug oder Trace- Klasse wird über Compilerschalter gesteuert. Diese werden durch Compileroptionen oder Präprozessorkonstanten gesetzt:
|
Aktiviern von |
C# Compileroption |
Präprozessorkonstante |
|---|---|---|
|
Debug |
/d:DEBUG |
#define DEBUG |
|
Trace |
/d:TRACE |
#define TRACE |
|
Phase |
Name |
Beschreibung |
|---|---|---|
|
1 |
Instrumentierung |
Verfolgungscode wird der Anwendung hinzugefügt |
|
2 |
Ablaufverfolgung |
Verfolgungscode schreibt Informationen in ein angegebenes Ziel |
|
3 |
Analyse |
Ablaufverfolgungscode wird ausgewertet |
Trace und Debug- Klasse haben einen fast identischen Aufbau

Mittels Kompilerschalter kann die Implementierung der Ablaufverfolgung komplett an oder abgeschaltet werden. Ist die Ablaufverfolgung implementiert, kann der Umfang der Protokollierung mittels von der Klasse Switch abgeleiteter Klassen fein gesteuert werden. Die Switche können durch eine Konfigurationsdatei gestellt werden.

Ein
Switch wird z.B. wie folgt eingesetzt:
public class BatchProcessor<TWorker> : IBatchProcessing
where TWorker : DMS.IWorker
{
// Protokollierung
private mko.CLog log;
TraceSwitch ts;
...
public BatchProcessor(mko.CLog log, TWorker worker)
{
this.log = log;
...
ts = new TraceSwitch("TraceBatchProcessor", "Diagnoseprotokolle des Batchprocessors");
}
...
void IBatchProcessing.pushJob(Job job)
{
try
{
lock (JobQueue) {
JobStorage.Add(job.JobId, job);
JobQueue.Enqueue(job);
job.SetWaiting();
Trace.WriteLineIf(ts.TraceInfo, string.Format("Job id= {0:D} in Warteschlange gestellt", job.JobId));
...
}
catch (Exception ex)
{
log.LogError(0, "PushJob: " + ex.Message);
Trace.Fail("PushJob: " + ex.Message);
}
}Konfigurieren kann man den Switch über die Konfigurationsdatei wie folgt:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<trace autoflush="true">
<!-- Trace- Klasse konfigurieren -->
<listeners>
<!-- Hinzufügen eines Listeners, der in eine Datei protokolliert-->
<add name="DemoListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="MyTrace.trc"/>
</listeners>
</trace>
<switches>
<!-- Protokollieren aller Meldungen aus der Stapelverarbeitung-->
<add name="TraceBatchProcessor" value="4"/>
</switches>
</system.diagnostics>
</configuration>Mit der Ereignisanzeige werden zentrale Protokolle über die Aktivitäten auf dem System geführt. NET stellt mit der Klasse EventLog eine leistungsfähige API für den Zugriff auf diese Protokolle bereit.
Nach der Installation von Windows existieren die drei Systemprotokolle Application, System und Security. Dieser Satz kann erweitert werden. Die Klasse EventLog erzeugt automatisch die neuen Protokolle, wenn dem Konstruktor der Name eines neuen Protokolls übergeben wird. Sollte die Ereignisanzeige bereits geöffnet sein, dann muß diese geschlossen und wieder neu geöffnet werden, um die neuen Protokolldateine sichtbar zu machen.
Um in einem Protokoll zu schreiben, muß zuvor eine Datenquelle registiert werden. Die Datenquelle ist ein beliebiger Name, welcher der Methode CreateEventSource übergeben werden muß. Nachdem eine Datenquelle angemeldet wurde, erfolgen alle mit ihr verknüpften Schreibvorgänge in dem Ereignisprotokoll, für das sie registriert wurde. Um diese Bindung an ein Ereignisprotokoll aufzuheben, muß die Quelle mittels DeleteEventSource gelöscht, und der Computer neu gebootet werden !

static void Main(string[] args)
{
try
{
// Create the source, if it does not already exist.
if (!System.Diagnostics.EventLog.SourceExists("Martin3"))
{
System.Diagnostics.EventLog.CreateEventSource("Martin3", "Application");
Console.WriteLine("CreatingEventSource");
}
System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
log.Source = "Martin3";
log.WriteEntry("Hallo- ein Eintrag");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}Namsspace: System.Net.Mail
Im .NET Framewaork gibt es zwei Implementierungen für Emailsverkehr: System.Web.Mail (.NET 1.1, veraltet) und System.Net.Mail (.Net 2.0, aktuell).
Emails werden als Objekte der Klasse MailMessage dargestellt.

Beispiel:
MailMessage email = new MailMessage(); // Adresse email.To.Add("viagra@junkmail.com"); // Betreff email.Subject = "vermülltes Postfach"; // Inhalt email.Body = "Liebe Spammer, ich habe Euch so lieb …";
Das Senden erledigt die Klasse SmtpClient.
try
{
// ...
// Im Konstruktor wird IP- Adr. und Port des Smtp- Servers übergeben, der die
// Email an das Zielpostfach weiterleiten soll
SmtpClient client = new SmtpClient("192.168.2.17", 25);
client.Send(email);
}
catch (Exception ex)
{
Debug.WriteLine("Beim Senden einer Mail: " + ex.Message);
}

Mittels der AppDomain- Klasse kann eine neue Anwendungsdomäne in einem Prozess erzeugt werden. Anschließend kann in dieser ein Objekt aus einer Assembly geladen werden.
static void Main(string[] args) {
Lib.MyClass obj1 = new Lib.MyClass();
obj1.PrintDomainName();
// Erzugen der neuen AppDomain
AppDomain newAppDomain = AppDomain.CreateDomain("newDomain");
// Instanziieren eines Objektes in der AppDomain und herstellten der Verbindung über einen Proxy (...Unwrap)
Lib.MyClass obj2 = (Lib.MyClass)newAppDomain.CreateInstanceAndUnwrap("NameDerAssemblyVonLibMyClass", "Lib.MyClass");
// Zugriff auf ein Objekt aus einer anderen Assembly (Remoting!)
obj2.PrintDomainName();
Console.ReadLine();
}Das Objekt, welches hier in der neuen Assembly instanziiert wurde, ist über AppDomain- Grenzen hinweg aufrufbar, da es von MarshalByRef Objekt abgeleitet ist.
// Inhalt der Assembly NameDerAssemblyVonLibMyClass.dll
namespace Lib
{
public class MyClass : MarshalByRefObject
{
public void PrintDomainName()
{
Console.WriteLine("AppDomain.FriendlyName= {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
}static void StarteU1HalloWelt()
{
// Create a new application domain.
AppDomain domain = System.AppDomain.CreateDomain("MyAppDomain");
// Try to execute the assembly.
try
{
// Laden und ausführen der Assembly u1-Hallo.exe,
// die sich im Verzeichnis .\Assemblies bedfindet
domain.ExecuteAssembly(@"Assemblies\u1-Hallo.exe");
}
catch (PolicyException e)
{
// Hatte die Assembly bei der Ausführung nicht die erfordelichen
// Rechte, dann wird wird eine PolicyException geworfen
Console.WriteLine("PolicyException: {0}", e.Message);
}
// Applicationsdomäne wird aus dem laufenden Prozess entladen
AppDomain.Unload(domain);
}Namespace: System.Delegat, System.MulticastDelegate
Delegates sind typisierte Funktionspointer. Durch sie wird es möglich, Einsprungpunkte von Methoden als Parameter einer Methode zu übergeben oder in einer Liste zu verwalten. Anwendung finden Delegates in Callback- Methoden bzw. zur Implementierung von Ereignissen.
Im .NET Framework sind Delegates nichts weiter als Objekte spezieller Klassen. Da jeder Delegate eine individuelle Parameterliste aufweisen kann, können die Delegates nicht alle Objekte einer gemeinsamen Klasse sein. Der C#/VB.NET- Kompiler generiert für jeden Delegate automatisch eine versigelte Klasse die von der Klasse System.Delegate abgeleitet ist. Diese Klassen haben folgenden Aufbau:

Jeder Delegate muß vor seiner Verwendung deklariert werden:
[Zugriffsmodifikator] delegate Methodendeklaration z.B.: // C#: Funktionszeigertyp von Handlern zur Behandlung des Arbeitsfortschritts- Event public delegate bool DGEventProgress(DirTreeProgressInfo info); ' VB.NET public Delegate Sub DGProgress(int count_
Im folgenden Beispiel wird mittels des Delegaten DGProgress für die Methode scanDir der Klasse CDirTree ein Callback implementiert, durch welche der Aufrufer fortlaufend über den Arbeitsfortschritt informiert wird.
class CDirTree {
public DGProgress CallBackProgress;
public void scanDir(string path) {
:
// Anzeige des Verarbeitungsstandes
if (icount_files % 100 == 0)
if (CallBackProgress != null)
CallBackProgress(icount_dirs, icount_files);
:
}
}Die Zuweisung einer Methode geschieht durch einen Konstruktor. Dieser Prozess wird auch als Registrierung bezeichnet:
class Class1 {
// Callback für Anzeige des Forschrittes
static void Progress(int anz_dirs, int anz_files)
{
Console.WriteLine("Progress: Dirs = {0}, Files= {1}", anz_dirs, anz_files);
}
static void Main() {
CDirTree dt = new CDirTree();
// Callbackroutine registrieren: #methode Progress wird dem Delegaten zugewiesen
dt.CallBackProgress = new DGProgress(Progress);
dt.traverse("c:\\");
}
Ein Delegate kann auch eine Liste von Funktionspointern speichern. Wird der Delegate aufgerufen, dann werden alle für den Delegate registrierten Methoden nacheinander ausgeführt. Die Registrierung erfolgt in diesem Fall mittels des += Operators:
class Class1 {
// Callback für Anzeige des Forschrittes
static void Progress(int anz_dirs, int anz_files)
{
Console.WriteLine("Progress: Dirs = {0}, Files= {1}", anz_dirs, anz_files);
}
// Callback für Anzeige des Forschrittes
static void Progress2(int anz_dirs, int anz_files)
{
Console.WriteLine("Progress2: Dirs = {0}, Files= {1}", anz_dirs, anz_files);
}
static void Main() {
CDirTree dt = new CDirTree();
// Zwei Callbackroutine registrieren:
dt.CallBackProgress += new DGProgress(Progress);
dt.CallBackProgress += new DGProgress(Progress2);
dt.traverse("c:\\");
}
Delegates erweitern die Möglichkeiten beim Aufruf von Methoden um Asynchrone Methodenaufrufe. Dazu werden neben der Synchronen Methode Invoke zum Aufruf einer Prozedur/Funktion auch asynchrone Methoden wie BeginInvoke und EndInvoke angeboten.

Collection werden im .NET durch Klassen gebildet, die Schnittstellen aus der folgenden Hierarchie implementieren:

Im
Namensraum System.Collections werden zur allgemeinen
Verwendung folgende Klassen bereitgestellt
|
ArrayList |
Queue |
Stack |
Hashtable |
|---|---|---|---|
|
implementiert IList |
implementier ICollection |
implementiert ICollection |
implementiert IDictionary |
|
Stellt Funktionalität eines gewöhnlichen Arrays dar, mit dem vorteil, daß Löschen von Elementen zu keinen Lücken führt. |
Realisiert einen FIFO- Speicher |
Realisiert einen Stapelspeicher |
realisert effiziente Liste mit zugriff über nichtnummerische Indizes (Schlüssel) |
ArrayList re_positionen = new ArrayList(); |
|
|
|


Beschreibt eine Schnittstelle von Collections, in denen Werte unter bestimmten Schlüsseln abgelegt werden:


Namespace: System.IO
Klasse enthält ausschließlich statische Methoden. Dient dem Zugriff auf Verzeichnisse.

Path enthält ausschließlich statische Member und dient zur Analyse von Dateipfaden:

Die Klasse File enthält ausschließlich statische Member. Sie liefert Funktionen zum Öffnen und Anlegen von Dateien.



Die Klasse dient zum byteweisen Lesen und Schreiben von Dateien
Mittels MemeoryStream kann ein byte[] - Array wie ein Datenstrom behandelt werden. In Kombination mit dem weiter unten behandelten BinaryReader können so Daten serialisert werden. Die Serialisierung in byte[]- Arrays ist besonders für die Übertragung in Netzwerken wichtig.
Zum Lesen und schreiben von typisierten Strömen bietet .NET sog. Reader und Writer- Klassen an. TextReader und TextWriter sind abstrakte Basisklassen, von denen Klassen zum Lesen bzw. Schreiben in Datenströmen oder Strings abgeleitet sind. Standard ist das Lesen und Schreiben von UTF8- Datenströmen. Durch übergabe einer Encoding- Klasse aus System.Text kann dieses Verhalten geändert werden.
BinaryReader und BinaryWriter ermöglichen das typisierte Lesen und Schreiben von und in binären Datenströmen.
Achtung: Der Unterschied zw. StreamWriter und BinaryWriter besteht darin, daß StreamWriter eine Unicode- Datei mit einer gültigen BOM (Byte Order Mark)
Im folgenden ist eine Liste gültiger Byte- Order Marken für Unicode- Dateien aufgelistet. Insbesondere XmlReader setzen diese vorraus!
|
Bytes |
Encoding Form |
|---|---|
|
00 00 FE FF |
UTF-32, big-endian |
|
FF FE 00 00 |
UTF-32, little-endian |
|
FE FF |
UTF-16, big-endian |
|
FF FE |
UTF-16, little-endian |
|
EF BB BF |
UTF-8 |

Achtung: Wenn die Leseposition mittels Seek des Streams geändert wird, der einem Reader zugrunde liegt, kommt es bei den folgenden Leseoperationen über den Reader zu undefinierten Verhalten !
Wenn die Leseposition in einem Stream geändert werden muß, dann ist der darauf operierende Reader nach der Positionierung auf dem Stream zu instanziiren wie folgt:
System.IO.Stream ReportStream = System.IO.File.Open(ReportFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); System.IO.StreamReader sr = new System.IO.StreamReader(ReportStream, System.Text.Encoding.ASCII); // Einlesen des Kopfes string Tabellenkopf = sr.ReadLine(); ... ReportStream.Seek(BeginByteOffset, SeekOrigin.Begin); StreamReader sr2 = new StreamReader(ReportStream, System.Text.Encoding.ASCII); string line = sr2.ReadLine();
In diesem Fall wird die Zeile korrekt ab BeginByteOffset gelesen. Wäre die Position im Stream geändert wurden, nachdem der Stream an den Reader gebunden wurde, wäre das Verhalten beim Lesen undefiniert.
Das .NET Framework enthält eine umfangreiche Bibliothek zum Analysieren, Lesen und Schreiben von XML- strukturierten Daten. Diese Bibliothek orientiert sich dabei an die vom w3c erlassen Standards.
XML- Daten können als Datenstrom mittels der Klassen XmlReader und XmlWriter- oder im Dokumentbaum (DOM) im Arbeitsspeicher mittels der Klasse XmlDocument verarbeitet werden.
Eine Validierung von Xml Daten gegen eine DTD oder gegen ein Schema wird durch die XmlReader- Klasse unterstützt.
Die Transformation von XML- Daten mittels XSL- Stylesheets wird in .NET 20 durch die Klasse System.Xml.Xsl.XslCompiledTransform realisiert.
Der XML 1.0 Standard schränkt den zulässigen Zeichensatz für Namen von Elementen und Attributen stark ein. Bei der Bildung von Namen sind neben den alphanummerischen Zeichen nur noch _ zugelassen. Werden z.B. Datenstrukturen in C# in XML serialisert, dann muß sichergestellt werden, das die resultierenden Element- und Attributbezeichner immer noch den XML-Einschränkungen genügen. Denn C# erlaubt z.B. auch Umlaute in den Bezeichnern.
Mittels der Klassen XMLConvert können Bezeichner in gültige XML- Bezeichner umgewandelt werden. Die Rückwandlung in das ursprüngliche Format wird ebenfalls unterstützt. Beispiel:
string txt = "<Hallo>+'Welt'@?$!__12:"; string HtmlCodiert = System.Web.HttpUtility.HtmlEncode(txt); Console.WriteLine("{0} =(Html)= {1}", txt, HtmlCodiert); string XmlCodiert = System.Xml.XmlConvert.EncodeName(txt); Console.WriteLine("{0} =(XmlName)= {1}", txt, XmlCodiert); Console.WriteLine("Dekodiert: {0}", System.Xml.XmlConvert.DecodeName(XmlCodiert)); XmlCodiert = System.Xml.XmlConvert.EncodeLocalName(txt); Console.WriteLine("{0} =(XmlLocalName)= {1}", txt, XmlCodiert); Console.WriteLine("Dekodiert: {0}", System.Xml.XmlConvert.DecodeName(XmlCodiert)); XmlCodiert = System.Xml.XmlConvert.EncodeNmToken(txt); Console.WriteLine("{0} =(XmlToken)= {1}", txt, XmlCodiert); Console.WriteLine("Dekodiert: {0}", System.Xml.XmlConvert.DecodeName(XmlCodiert));

Im Folgenden ist die Anwendung des XmlReaders gezeigt. Zu Beginn ist mittels der Calssfactory Create für ein XML ein Readerobjekt anzulegen. Über die XmlSettings kann ein Schema angegeben werden, gegen das das XML beim Lesen validiert werden soll. Für den Fall der Ungültigkeit kann in den Settings ein Ereignishandler registriert werden. Der Reader sollte am Ende seiner Nutzung immer mittels Close geschlossen werden, um verwaltete und Systemeignen Resourcen freizugeben.


namespace DMS
{
public class FeatureCollectorXml : DMS.FeatureCollector.IFeatureCollector
{
#region IFeatureCollector Member
XmlWriter writer;
public void Open(string path)
{
XmlWriterSettings sett = new XmlWriterSettings();
sett.Encoding = System.Text.Encoding.UTF8;
sett.Indent = true;
writer = XmlWriter.Create(path, sett);
}
public void Close()
{
writer.Close();
}
void DMS.FeatureCollector.IFeatureCollector.Collect(ref DMS.FeatureCollector.DmsMinDbDataSet FeatureData)
{
// Erzeugen von Xml- Dateien aus den Tabellen des DataSets mittels
// der Methoden WriteXml von DataSet und DataTable
DataSetToXml(FeatureData);
// Dokument einleiten -> XML Dekleration schreiben
writer.WriteStartDocument();
// Wurzelelement
writer.WriteStartElement("FeatureCollection");
foreach (DMS.FeatureCollector.DmsMinDbDataSet.FileInfosRow row in FeatureData.FileInfos.Rows)
{
writer.WriteStartElement("FileInfo");
// Attribute schreiben
writer.WriteAttributeString("sizeInBytes", row.SizeInBytes.ToString());
writer.WriteAttributeString("ext", row.ext);
writer.WriteAttributeString("ctime", row.ctime.ToString("s"));
// Inhalt des Elements schreiben
writer.WriteString(row.path);
writer.WriteEndElement();
}
// Wurzelelement schliessen
writer.WriteEndElement();
writer.WriteEndDocument();
}
#endregion
}
}DOM steht für Document Object Model und ist ein vom w3c entwickelte Standardschnittstelle für Strukturierte Dokumente wie XML (Das JavaScript im Internetexplorer implementierte bereites einen Vorläufer des DOM). Im DOM wird das strukturierte Dokument als eine Baumstruktur dargestellt, wobei die Elemente, Attribute, Instruktionen, Kommentare und Textinhalte als Knotenobjekte dargestellt werden. DOM definiert für die verschiedenen Knotentypen Klassen. Für den Zugriff auf die Konten werden vom DOM in den Kontoenklassen spezielle Methoden bereitgestellt.
Im NET wird mittels der Klasse XmlDocument ein DOM nach den Vorgaben des w3c für Xml- Dokumente bereitgestellt. Durch sie wird ein XML im Arbeitsspeicher als Dokumentenbaum repräsentiert. Im Unterschied zum XmlReader erhält man so zum einen Wahlfreien Zugriff auf die Knoten im Dokument, wird aber durch die größe des Arbeitsspeichers limitiert.
Im Folgenden ein Beispiel für die Baumdarstellung eines XML:
<?xml version="1.0" encoding="ISO8859-1" standalone="yes"?>
<!-- Ein Kommentar -->
<Planet name="Jupiter">
<Durchmesser Einheit="km" Wert="142984"/>
<Mond name="Io">
<Durchmesser Einheit="km" Wert="3642"/>
</Mond>
<Mond name="Europa">
<Durchmesser Einheit="km" Wert="3138"/>
Europa- ein Wasserplanet ?
</Mond>
<Mond name="Ganymede">
<Durchmesser Einheit="km" Wert="5262"/>
</Mond>
<Mond name="Callisto">
<Durchmesser Einheit="km" Wert="4800"/>
</Mond>
</Planet>Die entsprechende Baumdarstellung ist folgende:

Zum Anlegen eines Dokumentenbaumes dient die Klasse XmlDocument. Mittels der Load- Methoden kann ein Dom aus einer Xml- Datei oder String erzeigt werden. Die Eigenschaft DocumentElement ist der Dokumentenstamm, unter welchem sich Xml- Instruktionen, DTD, Kommentare und Wurzelelement befinden. DocumentElement ist damit der Einstieg in den Dokumentenbaum.

Alle Klassen den DOM sind von der Basisklasse XmlNode abgeleitet. Elemente Attribute oder Kommentare lassen sich damit letztendlich als XmlNodes betrachteten. Der DOM- Dokumentenbaum ist ein Baum aus XmlNodes:

Jeder
Dokumentknoten hat die Eigenschaften und Methoden von XmlNode, diese
müssen aber nicht in jedem Fall einen sinnvolle Implementierung
besitzten. Beispielsweise besitzen Kommentarknoten keine Namen.

Die
Eigenschaft NodeType zeigt an, zu welcher speziellen
Knotenklasse ein Knotenobjekt gehört. Folgende Werte sind
möglich:
|
NodeType |
Knotenklasse |
|---|---|
|
Attribute |
Attributknoten |
|
CDATA |
CDATA- Abschnitt <!CDATA[[ Hallo <xml> Welt ]]> |
|
Comment |
Kommetare <!-- Hallo --> |
|
Document |
Stammknoten eines XmlDocument |
|
DocumentFragment |
Dokumentfragment |
|
DocumentType |
DTD- Abschnitt einer XML- Datei |
|
Element |
Elementknoten |
|
Entity |
Entity- Deklaration <!ENTITY ...> |
|
Notation |
|
|
ProcessingInstruction |
Anweisungen an den Parser <?xml ...> |
|
Text |
Textknoten |
XmlElement ist eine spezialisierte Klasse für Xml- Elemente. Sie bietet Methoden und Eigenschaften zum komfortabelen Zugriff auf die Attribute eines Elements. Zudem können mittels GetElementByTagName - Methode Teilmengen von Kindknoten bestimmt werden.



static void Main(string[] args)
{
// Dokumentbaum aufbauen
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Properties.Settings.Default.xmlDoc);
// Für den Knotenzugriff über XPath wird ein XPathNavigator- Objekt benötigt.
// Die Dokumentwurzel ist der Bezugspunkt für weitere Navigationen
XPathNavigator navi = xmlDoc.CreateNavigator();
// Werden XML- Vokabulare in Namensräumen eingeschlossen, dann sind die
// Namensraumtabellen des NAvigators in einem Namespace- Manager zu
// registrieren
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(navi.NameTable);
// Im Namespacemanager werden die eingewsetzten Namensräume verzeichnet
namespaceManager.AddNamespace("astro", "http://www.tracs.de/Planeten.xsd");
// Auswahl eines Kontens in einem Xml Dokument über einen XPath- Ausdruck.
// Das Ergebnis ist wieder vom Typ XPath- Navigator. Der selektierte Knoten
// ist der neue Bezugspunkt für weitere Navigationen.
string xpath = "//astro:Durchmesser";
XPathNavigator node = navi.SelectSingleNode(xpath, namespaceManager);
Console.Write("Gefundener Knoten\n{0}\n", node.OuterXml);
xpath = "//astro:Durchmesser[@Wert=\"4800\"]";
node = navi.SelectSingleNode(xpath, namespaceManager);
Console.Write("Gefundener Knoten\n{0}\n", node.OuterXml);
Ancestors(node);
xpath = "//astro:Planet[@name = \"Jupiter\"]/astro:Mond/astro:Durchmesser[@Wert < \"5000\"]";
XPathNodeIterator NodeSelection = navi.Select(xpath, namespaceManager);
Console.WriteLine("Von {0} selektierte Knoten:", xpath);
foreach (XPathNavigator naviNode in NodeSelection)
{
Console.WriteLine(naviNode.OuterXml);
Console.WriteLine("Elternknoten:");
naviNode.MoveToParent();
Console.WriteLine(naviNode.OuterXml);
}
}
private static void Ancestors(XPathNavigator node)
{
// Alle Knoten bis zurück zur Wurzel
XPathNodeIterator iterator = node.SelectAncestors(XPathNodeType.Element, false);
Console.WriteLine("Alle Knoten bis zurück zur Wurzel");
foreach (XPathNavigator parentNode in iterator)
{
Console.WriteLine(parentNode.OuterXml);
}
} static void Main(string[] args)
{
try
{
XslCompiledTransform xsltTrafo = new XslCompiledTransform(true);
// Stylesheet laden
xsltTrafo.Load(Properties.Settings.Default.XsltTabPlanetenMonde);
// Staistik zu den bei der Compilation erzeugten temporären Dateien
Console.WriteLine("Basisverzeichnis für temp. Dateien: {0}", xsltTrafo.TemporaryFiles.BasePath);
Console.WriteLine("Temporäres Verzeichnis: {0}", xsltTrafo.TemporaryFiles.TempDir);
Console.WriteLine("Anz. temporärer Dateien: {0:D}", xsltTrafo.TemporaryFiles.Count);
// Transformation des XML- Dokuments in ein Html Dokument mittels des vorkompilierten Stylesheets
xsltTrafo.Transform(Properties.Settings.Default.XmlDoc, "Tabelle-der-Palneten-und-Monde.html");
}
catch (Exception ex)
{
Console.WriteLine("Fehler: " + ex.Message);
}
}In natürlichen Sprachen werden mittels Attribute Eigenschaften von Substantiven festgelegt wie heißer Tee oder grüne Farbe. Analog werden durch Attribute in C# zusätzliche Informationen zu Klassen, Prozeduren und Variablen hinterlegt. Diese Informationen können dann vom Compiler verarbeitet werden, oder werden als Metadaten in der Assembly gespeichert.
Beispiel:
Definieren von Metadaten ür die Assembly:
[Assembly: AssemblyTitle("GeoInfo")]
[Assembly: AssemblyDescription("Zugriff auf Datenbank")]
[Assembly: AssemblyCompany("mkoSoft")]
:
[Assembly: AssemblyVersion("1.0.*")]Attribute sind Klassen, die von System.Attribute abgeleitet sind. Jedes Attribut ist selbst wieder mit dem Basisattribut AttributeUsage ausgezeichnet. Damit ergibt sich folgende Grundstruktur für eine Attributdeklaration:
[AttributeUsage(AttributeTargets, // Elemente, die attributiert werden können
Inherited, // Wird das Attribut vererbt
AllowMultiple)] // Kann ein Element mehrfach attributiert werden
public Class MyAttriubute : Attribute {
// individuelle Klassenmember
public string name;
public string id;
}Allgemein sind mit Attributen beliebige Elemente in .NET wie Assembly, Klasse und Member attributierbar. Über den Parameter AttributeTargets in AttributeUsage kann die Menge der attributierbaren Elemente eingeschränkt werden.
Die Attributierung eines Elements erfolgt beispielsweise so:
[MyAttribute(name="mko", id="1")]
public Class Anwendung {
[MyAttribute(name="mak", id="2")]
int TueWas() {
return 99;
}
}In der Parameterliste der Attributdefinition können Felder in einer Kommaseparierten Liste dierekt Werte zugewiesen werden.
Die Klasse System.Attribute stellt statische Methoden Attribute.GetCustomAttribute(..) und Attribute.GetCustomAttributes(...) bereit. Diese liefern eine Referenz auf ein Attribut vom Typ object bzw. eine Referenz auf ein Array mit Attributen zurück. Als Eingaben erwarten die Funktionen das Type- Objekt der Instanz, deren Attribute ausgelesen werden soll, sowie den Typ des auszulesenden Attributes.
// Fall: Die Klasse Anwendung besitzt nur ein Attribut vom Typ MyAttribute
MyAttribute myAtt = (MyAttribute) Attribute.GetCustomAttribute(typeof(Anwendung), typeof(MyAttribute));
if (myAtt != null) {
Console.WriteLine("name= {0}", myAtt.name);
Console.WriteLine("id = {0}", myAtt.id);
}