![]() |
[BorCon] Einführung in die .NET Framework Klassen Bibliothek
Ich habe von Corbin Dunn die schriftliche Genehmigung seinen Artikel
![]() ...:cat:... |
Re: Einführung in die .NET Framework Klassen Bibliothek
Einführung in die .NET Framework Klassen Bibliothek von Corbin Dunn (BorCon 2004) Borland R&D Software Engineer Die .NET Framework Klassen Bibliothek (FCL) besteht aus einer Reihe von Klassen, Schnittstellen (Interfaces) und Wertetypen welche in eigenen Anwendungen eingesetzt werden können. Das .NET Framework bietet Typen die einem folgende Möglichkeiten bieten:
Alle Typen der FCL sind kompatibel zur Common Language Specification (Allgemein Sprachspezifikationen, CLS). Das ermöglicht es jeder CLS-kompatiblen Entwicklungsumgebung (z.B. Delphi, C# und VB) diese Typen zu nutzen. |
Re: Einführung in die .NET Framework Klassen Bibliothek
Basis Namensräume und Typen der FCL Jede Klasse ist in einem Namensraum (Namespace) untergebracht, welcher ihren Zweck sinnvoll beschreibt, und im .NET Framework gibt es viele Namensräume. A grundlegender, vollständig qualifizierter Klassenname ist System.Collections.ArrayList. Der Teil links vom letzten Punkt ist i.A. der Namensraum, der rechte Teil ist der Klassenname (Notiz 1). Alle Klassen der FCL sind im Root-Namensraum System untergebracht. Klassen von Drittanbietern sollten, wenn möglich, innerhalb eines Namensraum mit folgendem Format Firmenname.Technologiename untergebracht sein. Zum Beispiel: Borland.Delphi.System ist der vollständig qualifizierte Name der Delphi System Unit. Die FCL basiert auf dem Common Type System (allgemeines Typensystem, siehe CLS). Das Common Type System ermöglicht es der FCL mit verschiedenen Sprachen in der "verwalteten Laufzeitumgebung" (Managed Runtime) zu operieren. Wertetypen (Value Types) vs. Refenztypen (Reference Types) Wertetype sind direkt für ihre Werte verantwortlich und i.A. auf dem Stack hinterlegt. Referenztypen speichern eine Referenz (Zeiger, Verweis) auf deren Daten und sind auf dem Heap gesichert. Wertetypen sind allgemein primitive Typen, wie z.B. ordinale Typen. Referenztypen hingegen sind i.A. Objekte, wie z.B. System.Windows.Forms.Control. Die Änderung eines Werte in einem Wertetypen verändert nur die spezielle Instanz dieses Werte, da diese eine Kopie ihres Wertes enthalten. Folgendes C# Beispiel soll das verdeutlichen:
Code:
In der zweiten Zeile wird secondPoint der Wert von firstPoint zugewiesen; es wird eine Kopie des Werte von firstPoint hinterlegt. Dieser hat die Werte X=5 und Y=5. In der dritten Zeile wird firstPoint.X der Wert 6 zugewiesen. Das verändert nur den Wert in firstPoint.X, secondPoint.X hält weiterhin den Wert 5.
Point firstPoint = [b]new[/b] Point(5, 5);
Point secondPoint = firstPoint; firstPoint.X = 6; Betrachten wir nun folgenden C# Code:
Code:
In der zweiten Zeile wird secondControl ein Verweis auf auf firstControl zugewiesen. Die Veränderung des Wertes firstControl.BackColor in der dritten Zeile verändert daher auch den Wert in secondControl.BackColor da der Wert von Typ Control ein Referenzwert ist.
Control firstControl = [b]new[/b] Control();
Control secondControl = firstControl; firstControl.BackColor = Color.Red; In der FCL sind alle Basistypen, wie z.B. Integer und Boolean (auch Aufzählungen), Wertetypen. Fast alle anderen Klassen sind in der Regel Referenztypen, so auf System.String. In der Programmierung wird ein Wertetyp durch struct (in C#) oder record (in Delphi) erstellt, Referenztypen hingegen werden mit Hilfe von class erstellt. Das Boxing von Typen Es ist in .NET möglich Wertetypen in Referenztypen (und zurück) umzuwandeln. Dazu folgendes C# Beispiel:
Code:
In der dritten Zeile wird der Wertetype implizit als Referenztyp "geboxt". In C# kann man Objekttypen nicht Wertetypen zuweisen, deshalb würde die vierte Zeile auch einen Compiler-Fehler provozieren. Man muss den Wert explizit als Wertetyp darstellen, dadurch wird der Wert kopiert.
[b]object [/b]obj = null; [i]// a reference type[/i]
[b]int [/b]i = 5; [i]// a value type[/i] obj = i; [i]// boxing[/i] [i]//i = obj; // compiler error[/i] i = ([b]int[/b])obj; [i]// unboxing: the value type is copied to i[/i] Beachte, dass Delphi selbst als sehr typensicher gilt und betrachte den folgenden (zu oben analogen) Delphi.NET Quellcode:
Delphi-Quellcode:
Obiger Code lässt sich in Delphi nicht kompilieren, da in der zweiten Zeile die Typensicherheit nicht gewährleistet wird. In diesem Fall gibt es für den Delphi-Programmierer zwei Lösungsmöglichkeiten. Entweder durch explizites casten des Wertes in ein TObject:
var
I: Integer; Obj: TObject; begin I := 5; Obj := I; // Compiler error: Incompatible types // I := Obj; // Expected compiler error I := Integer(Obj); end.
Delphi-Quellcode:
oder durch den Compilerschalter AUTOBOX, welcher irgenwo vor der "Problemzeile" erscheinen muss:
Obj := TObject(I);
Delphi-Quellcode:
Der Compilerschalter AUTOBOX zwingt den Delphi-Compiler weniger typensicher zu agieren und lässt Delphi so etwas mehr wie C# erscheinen. Die Nutzung kann die Programmierung erleichtern, allerdings kann es auch schneller zu Programmierfehlern führen.
{$AUTOBOX ON}
Schnittstellentypen (Interface Types) In der FCL existieren viele Interfacetypen und diese kann man auch in gewohnter Art und Weise einsetzen. Interfaces sind abstrakte Deklarationen welche durch Klassen (Referenztypen) implementiert werden. (Notiz 1) Eine Ausnahme zu dieser Regel sind verschachtelte Typen. |
Re: Einführung in die .NET Framework Klassen Bibliothek
System.Delegate und System.EventHandler Die FCL bietet Delegate, diese sind verwaltete Zeiger (managed pointer) welche eingesetzt werden können um Ereignishandler und Callback-Funktionen im .NET Framework zu nutzen. Die Standardimplementierung von System.Delegate ermöglicht es einem Delegat hinzuzufügen bzw. zu entfernen, sowie die darin enthaltenen Methoden. System.Delegate ist die Basisklasse für alle Delegate. System.MulticaseDelegate ist von System.Delegate abgeleitet und bietet die Möglichkeit mehrere Methode aufzurufen. Wenn ein Delegat nicht von System.MulticastDelegate abgeleitet ist, so kann es nur max. eine Referenz auf eine Methode enthalten. Eigene Delegate erstellen Jeder .NET compiler sollte die Unterstützung von Delegaten über Schlüsselwörter oder eine spezielle Syntax unterstützen. C# bietet das Schlüsselwort delegate:
Code:
Beachte, dass der Delegate-Name auf EventHandler endet. Das ist ein Standard und Microsoft empfiehlt alle Delegate so zu benennen, es ist jedoch keine Pflicht.
[b]public delegate void [/b]SampleEventHandler([b]object [/b]sender, EventArgs args);
Weiterhin gilt es zu beachten, dass die Signatur des Delegats keinen Rückgabewert hat, der erste Parameter vom Typ object ist und der zweite Parameter vom Typ EventArgs. Diese Signatur trifft man sehr häufig in der FCL an und wird auch von Microsoft empfohlen, wiederum ist auch dieses keine Verpflichtung. Wie auch immer, diese Signatur wird so häufig genutzt, dass sie ihr eigenes Delegat besitzt: System.EventHandler. Durch Festlegung ist der erste Parameter immer die Instanz, welche die Methode aufgerufen hat. Der zweite Parameter enthält die Argumente, welche das Ereignis beschreiben. Wenn man mehr Informationen an ein Delegat weiterleiten möchte, kann man eine eigene Klasse von EventArgs ableiten und die benötigten Eigenschaften darin definieren:
Code:
Wenn das Delegat aufgerufen wird, dann kann die aufgerufene Methode auf args.MySpecialValue zugreifen.
[b]public class [/b]MyEventArgs : EventArgs
{ [b]public [/b]MyEventArgs(string mySpecialValue) { MySpecialValue = mySpecialValue; } [b]public string[/b] MySpecialValue; } [b]public delegate void[/b] SampleSpecialEventHandler([b]object [/b]sender, MyEventArgs args); Einen eigenen Delegat in Delphi zu erstellen ist recht einfach und bekannt, einfach die bekannte Methodentypendeklaration nutzen:
Delphi-Quellcode:
Erstellen und Nutzen eines Delegates
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs); In C# erstellt man ein Multicast-Delegat mit Hilfe des Schlüsselwortes event:
Code:
Will man einem Ereignis einen (weiteren) Ereignishandler zufügen, so muss man in C# die += Syntax nutzen:
[b]public event [/b]SampleEventHandler EventTest;
Code:
Der C# Compiler erkennt die Zuweisung und ersetzt die += Syntax intern durch einen Aufruf zu System.Delegate.Combine, um SampleEventHandler der Liste in EventTest hinzuzufügen. Es ist in C# möglich dieses Konstrukt auch zu erweitern und wie folgend zu schreiben:
[b]private void [/b]WinForm_Load([b]object [/b]sender, System.EventArgs e)
{ EventTest += [b]new [/b]SampleEventHandler(OnMyEvent); } [b]private void [/b]OnMyEvent([b]object [/b]sender, EventArgs args) { MessageBox.Show("OnMyEvent called!"); }
Code:
Beachte die Nutzung von Delegate.Combine und die implizite Übergabe des Wertetypen als Parameter.
// Example of how to "roll out" an event handler
[b]private [/b]SampleEventHandler otherEvent; [b]private event [/b]SampleEventHandler AnotherEventTest { [b]add[/b] { otherEvent = (SampleEventHandler)Delegate.Combine(otherEvent, value); } [b]remove[/b] { otherEvent = (SampleEventHandler)Delegate.Combine(otherEvent, value); } } Die allgemeine Art einen einfachen Ereignisshandler in Delphi zu schreiben sieht wie folgend aus:
Delphi-Quellcode:
Um mehrfache (multi-cast) Ereignishandler zu schreiben muss man die Schlüsselwörter add und remove nutzen:
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs); TWinForm18 = class(System.Windows.Forms.Form) private FSingleSampleEventHandler: TSampleEventHandler; property SampleEventHandler: TSampleEventHandler read FSingleSampleEventHandler write FSingleSampleEventHandler; end;
Delphi-Quellcode:
Anstelle der (neuen) Standardsyntax für Mehrfach-Ereignishandler (hier FMultiSampleEventHandler) kann man die Implementierung auch manuell gestallten:
type
TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs); TWinForm18 = class(System.Windows.Forms.Form) private FMultiSampleEventHandler: TSampleEventHandler; property MultiSampleEventHandler: TSampleEventHandler add FMultiSampleEventHandler remove FMultiSampleEventHandler; end;
Delphi-Quellcode:
Man beachte den Einsatz von @, um die Adresse des Ereignisses zu ermitteln; wird @ nicht genutzt, wird der Ereignishandler direkt aufgerufen. Man beachte auch den Cast des Ereignistypen, genauso wie auch in C#.
FMultiSampleEventHandlerByHand: TSampleEventHandler;
{ Multi-cast event handler roll out methods } procedure AddMultiEvent(Value: TSampleEventHandler); procedure RemoveMultiEvent(Value: TSampleEventHandler); { Multi-cast event, rolled out by hand } property MultiSampleEventHandlerByHand: TSampleEventHandler add AddMultiEvent remove RemoveMultiEvent; // Die Implementierung gestalltet sich etwas umständlich, wenn man die Syntax dafür nicht kennt: procedure TWinForm18.AddMultiEvent(Value: TSampleEventHandler); begin FMultiSampleEventHandlerByHand := TSampleEventHandler( Delegate.Combine(@FMultiSampleEventHandlerByHand, @Value)); end; procedure TWinForm18.RemoveMultiEvent(Value: TSampleEventHandler); begin FMultiSampleEventHandlerByHand := TSampleEventHandler( Delegate.Remove(@FMultiSampleEventHandlerByHand, @Value)); end; In Delphi kann man weiterhin die Standard Art nutzen, um einen Ereignishandler zu setzen:
Delphi-Quellcode:
Will man jedoch auf mulit-casts nicht verzichten, so muss man Include(...) nutzen:
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
e: System.EventArgs); begin SampleEventHandler := OnSampleEvent; end;
Delphi-Quellcode:
Um einen Ereignishandler zu entfernen muss man Exclude(...) nutzen.
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
e: System.EventArgs); begin Include(MultiSampleEventHandler, OnSampleEvent); end; Der Aufruf der Ereignishandler gestaltet sich in Delphi ähnlich wie in C#. Zuerst muss man überprüfen, ob der Handler nil ist, anschließend ruft man diesen wie eine Methode auf:
Code:
[i]// C# Syntax[/i]
[b]if [/b](EventTest != [b]null[/b]) EventTest(this, [b]new [/b]EventArgs());
Delphi-Quellcode:
Notiz meinerseits: "delegate" (englisch) wird im Deutschen i.A. mit "Delegate(n)" übersetzt und ist nichts weiter als ein schönes Wort für "statische Callback Funktionen"
// Delphi Syntax
if Assigned(FSingleSampleEventHandler) then FSingleSampleEventHandler(Self, EventArgs.Create); |
Re: Einführung in die .NET Framework Klassen Bibliothek
Attribute und der System.Attribute Typ Attribute werden genutzt, um Typen und Typen-Members (z.B. Felder, Eigenschaften, Methoden) zu "dekorieren". Attribute werden als Metadaten innerhalb einer .NET Framework Anwendung (oder Assembly) gesichert. Zur Laufzeit kann man Typen, Typen-Members und Instanzen auf das Vorkommen solcher Attribute hin überprüfen. Die generelle Syntax für Delphi und C# ist im Grunde gleich und kommt direkt vor dem Symbol, auf welches das Attribut angewandt wird:
Code:
Hinweis: Es ist definiert, dass Attributnamen immer auf das Wort Attribute enden (z.B.: DesignerAttribute, ComVisibleAttribute). Die meisten Compiler erlauben es einem den Teil "Attribute" wegzulassen.
[AttributeTypeName(Parameter, Parameter)]
[b]public class [/b]Foo { ... } Was kann man nun mit diesen Attributen anfangen? Die meisten Entwicklungsumgebungen bieten spezielle Design-Time Klassen, welche es einem ermöglichen eine Komponente/ein Control in der Oberfläche (zur Designzeit) zu modifizieren. Dazu dient (in .NET) zum Beispiel das DesignerAttribute:
Code:
Wenn der WinForms Designer einen Designer für ein Control erstellt, stößt dieser auf das [i]DesignerAttribute[i] der Klasse und erstellt einen MyControllDesigner für diese.
[DesignerAttribute("MyControlDesigner")]
[b]public class [/b]MyControl : System.Windows.Forms.Control { … } Ein weiteres gutes Beispiel ist ComVisibleAttribute. Man kann .NET Assemblies dem COM Subsystem zur Verfügung stellen, indem man diese mit regasm.exe registriert. Jeder Typ, welcher mit ComVisibleAttribute markiert ist wird damit dem COM Subsystem zugänglich:
Code:
Man kann sich jetzt die Frage stellen, ob man jetzt jeden Typ so markieren muss, der dem COM Subsystem zur Verfügung gestellt werden soll. Die Antwort ist: nein. Man kann dieses Attribut auch dem gesamten Assembly zur Verfügung stellen:
[ComVisible(true)]
[b]public interface [/b]MyInterface { }
Code:
Assembly-Level Attribute können auch für andere Dinge genutzt werden (z.B.: Version, Autor, Firmenname, etc.):
[assembly: ComVisible(true)]
Code:
Attribute abfragen
[assembly: AssemblyTitle("This is my cool assembly")]
[assembly: AssemblyDescription("It does neat stuff")] [assembly: AssemblyCompany("Borland Software Corporation")] [assembly: AssemblyVersion("1.0.3.5")] Um Attribute für einen Typen oder eine Instanz zu ermitteln kann man sich der Reflection und des TypeDescriptor bedienen:
Delphi-Quellcode:
Hinweis: Man kann eine Instanz (z.B: Self in Delphi oder this in C#) oder auch einen Sysmte.Type an TypeDescriptor.GetAttributes übergeben. Im Hintergrund wird immer auf den Typ zugegriffen, da Attribute Metadaten sind und diese im Typ, nicht in der Instanz, gesichert sind.
var
Attributes: AttributeCollection; begin Attributes := TypeDescriptor.GetAttributes(Self); Nachdem man Zugriff auf die AttributeCollection erlangt hat, kann man diese recht einfach durchlaufen:
Delphi-Quellcode:
Hinweis: Hier wurde die neuere "Delphi 9" for-Schleifen-Syntax genutzt. In Delphi 8 muss man den Iterator manuell erstellen und durchlaufen.
for AnAttribute in Attributes do
begin TextBox1.Text := TextBox1.Text + AnAttribute.ToString + #13#10; end; Wenn man nur auf ein bestimmtes Attribute zugreifen möchte, so kann man das wie folgend tun:
Delphi-Quellcode:
Man beachte, dass null zurückgeliefert wird, wenn der Typ keine Attribute besitzt.
var
Attributes: AttributeCollection; DefaultEventAttr: DefaultEventAttribute; begin ... DefaultEventAttr := DefaultEventAttribute(Attributes[TypeOf(DefaultEventAttribute)]); if DefaultEventAttr <> nil then Text := 'Has a Default Event name of: ' + DefaultEventAttr.Name; Wenn man die Attribute eines bestimmten Mitgliedes (Type-Member) ermitteln möchte, muss man Reflection nutzen, um das Mitlgied zu finden. Anschließend kann man Attribute.GetCustomAttributes(..) nutzen, um auf die Attribute des Mitgliedes zuzugreifen:
Delphi-Quellcode:
Wenn man Attribute.GetCustomAttributes(..) nutzt, kann man einen zweiten Parameter übergeben, um auch die Vorgängertypen zu untersuchen. In diesem Beispiel würde es nichts ändern, da diese Methode keine Vorgänger hat.
[TMyCustomMethodAttriute]
procedure TMainForm.Button1_Click(sender: System.Object; e: System.EventArgs); var MyType: System.Type; ClickMethod: MethodInfo; MethodAttrs: array of Attribute; MethodAttr: Attribute; I: Integer; begin { Use reflection to get the Button1_Click member} MyType := GetType; ClickMethod := MyType.GetMethod('Button1_Click', BindingFlags.NonPublic or BindingFlags.Instance); MethodAttrs := Attribute.GetCustomAttributes(ClickMethod); TextBox1.Text := '--- Method Attributes --- '#13#10; for I := 0 to Length(MethodAttrs) - 1 do begin MethodAttr := MethodAttrs[I]; TextBox1.Text := TextBox1.Text + MethodAttr.ToString + #13#10; end; Eigene Attribute Es ist recht einfach eigene Attribute zu erstellen. Man muss nur wenige Dinge beachten. Am wichtigsten ist, das jedes Attribute direkt oder indirekt von System.Attribute abgeleitet werden muss:
Delphi-Quellcode:
Hinweis: Bachten Sie, dass das AttributeUsage Attribute dem neuen Attribute hinzugefügt wurde. Das teilt dem Compiler mit, dass dieses Attribute auch als solches genutzt werden darf.
{ This can only be applied to methods }
[AttributeUsage(AttributeTargets.Method)] TMyCustomMethodAttriute = class(Attribute) end; |
Re: Einführung in die .NET Framework Klassen Bibliothek
System.Object Es ist wichtig die Basisklasse aller Klassen zu nennen: System.Object. Delphi und C# leiten alle Klassen implizit von Object ab. Es gibt vier wichtige Methoden von Object welche genannt werden sollten: Equals, Finalize, GetHashCode und ToString. System.Object.Equals(...) The Methode Equals wird genutzt, um zwei Objekte miteinander zu vergleichen. Diese Methode kann überschrieben werden, wenn man feststellen will, ob zwei Objekte identisch sind. Die Standardimplementierung liefert true zurück, wenn die Referenzen zweier Objekte identisch sind. Anders ausgedrückt, Equals überprüft nicht, ob die Werte identisch sind. Ausnahme zu dieser Regel sind Wertetypen, diese werden als identisch betrachtet, wenn deren Bits identisch sind. System.Object.GetHashCode(...) GetHashCode wird immer aufgerufen, wenn ein Objekt in eine Hashtabelle eingefügt wird. Allgemein: wenn man GetHashCode überschreibt, dann sollte man auch immer Equals überschreiben, ansonsten kann es zu Problemen bei der Nutzung in Hashtabellen kommen. GetHashCode sollte immer sehr performant sein und einen Integerwert zurückliefern, welcher das Objekt (fast) eindeutig identifizieren kann. Kommt es bei den HashCode-Werten zu Kollisionen, so wird Equals aufgerufen, um zwei Objekte miteinander zu vergleichen; das ist auch der Grund, warum Equals überschrieben werden sollte, wenn man GetHashCode überschreibt.. System.Object.Finalize(...) Finalize ist der Basis-Destructor für Objekte. Finalize wird immer automatisch aufgerufen, wenn ein Objekt der Garbage-Collection hinzugefügt wird, außer man ruft explizit die Methode SurpressFinalize(...) auf. In Delphi wird Finalize durch Destroy, in C# durch ~ClassName definiert. Da jede Sprache bereits ihre eigene Art hat die Finalize Methode zu spezifizieren, erlauben diese es auch nicht Finalize zu überschreiben. Es gibt einige wichtige Punkte, welche bei Finalize zu beachten sind. Erstens, man kann nicht vorhersagen wann die Methode aufgerufen wird. Wenn es wichtig ist, dass Resourcen wieder freigegeben werden, dann sollte man sich NIE auf Finalize/Destroy verlassen. Stattdessen sollte man die Schnittstelle IDisposable implementieren und die Resourcen in der Dispose(...) Methode freigeben. Jeder, der das Objekt dann einsetzt sollte Dispose aufrufen, um die Resourcen wieder freizugeben. Für den Fall, das es jemand vergessen sollte Dispose aufzurufen, sollte man Dispose auch aus Finalize heraus aufrufen. Es ist aber sicher zu stellen, dass Dispose nicht bereits ausgeführt wurde. Das sichert einen dagegen ab Resourcen ausversehen auf Dauer zu belegen. System.Object.ToString(...) Eine sehr praktische Methode aller Objekte ist ToString. Diese Methode ermöglicht es jedem Objekt sich als String zu repräsentieren. Es ist z.B. sehr praktisch, um Integerwerte als Strings darzustellen; einfach .ToString(...) ans Ende anfügen und schon hat man die Stringdarstellung eines Integers. Für eigene Objekt kann es sinnvoll sein ToString zu überschreiben und damit eine gute Darstellung des Objektes als String zu erzielen. |
Re: [BorCon] Einführung in die .NET Framework Klassen Biblio
System.String und System.Text.StringBuilder Es ist wichtig zu wissen, dass eine Instanz von System.String nie modifiziert werden kann. Das heißt, einmal erstellt, kann der enthaltene String nicht mehr geändert werden. Alle Anweisungen, welche den Anschein haben den String zu ändern erstellen im Hintergrund eine neue String-Instanz und liefern diese zurück. Während eine augenscheinlich einfache Anweisung wie:
Delphi-Quellcode:
auf den ersten Blick gut aussieht, so wird man in einer Schleife schnell feststellen, dass dem nicht so ist. Der Grund? Der String Foo muss mit jeder Änderung neu erstellt werden und das kann sehr langsam sein.
Foo := Foo + 'Another String';
The Lösung ist denkbar einfach, man kann die Klasse System.Text.StringBuilder nutzen:
Delphi-Quellcode:
Hinweis: In diesem einfachen Beispiel wird man kaum Performanceunterschiede bemerken, aber in einer langen und engen Schleife wird man! Wenn man Zweifel hat, sollte man wahrscheinlich immer auf die Klasse System.Text.StringBuilder zurückgreifen.
var
Attributes: AttributeCollection; AnAttribute: Attribute; DefaultEventAttr: DefaultEventAttribute; MyBuilder: StringBuilder; begin { The above implementation of writing to the text box string is slow since strings are immutable (can't be changed) } { Instead of simply performing string concatenations, it is more performant to use a StringBuilder class } Attributes := TypeDescriptor.GetAttributes(Self); MyBuilder := StringBuilder.Create; MyBuilder.Append(' -- All Attributes on the Type ---'#13#10); for AnAttribute in Attributes do begin MyBuilder.Append(AnAttribute.ToString); MyBuilder.Append(#13#10); end; TextBox1.Text := MyBuilder.ToString; end; |
Re: [BorCon] Einführung in die .NET Framework Klassen Biblio
Einige meiner Lieblingsklassen System.Collections.ArrayList Oft benötige ich eine liste von Objekten und die ArrayList ist geradezu perfekt für solche Situationen. Es gibt verschiedene Situationen, für welche ich die ArrayList nutze. Oft kopiere ich zum Beispiel die Inhalt einer ArrayListe in ein Array Objekt:
Delphi-Quellcode:
Der Vorteil dieser Methode ist, dass man den gesamten Inhalt der ArrayListe in einem typisierten Array hat. Man beachte, dass dieses Beispiel zur Laufzeit einen Ausnahmefehler erzeugen würden, wenn in der ArrayListe andere Werte als Strings enthalten sind. Desweiteren muss immer sicher gestellt sein, dass das Array groß genügend ist, um die Werte der ArrayListe aufzunehmen. In obigem Beispiel wird das durch den Aufruf zu SetLength sicher gestellt.
var
MyList: ArrayList; MyStrs: array of string; begin MyList := ArrayList.Create; MyList.Add('Hello'); MyList.Add('World'); MyList.Add('Foo'); SetLength(MyStrs, MyList.Count); MyList.CopyTo(MyStrs); end; Ein weiterer wichtiger Punkt ist, das ein dynamisches Delphi-Array (hier: "array of string") und das System.Array des .NET Framework äquivalent sind. Ich nutze die ArrayList auch desöfteren, um Elemente zu sortieren. Die Methode [i]Sort[i] ist die Standardimplementierung, um diese Aufgabe zu erledigen und meist reicht diese auch aus. Sollte man jedoch Objekte haben, welche sich nicht einfach sortieren lassen, so kann man die Schnittstelle IComparer implementieren und diese an ArrayList.Sort(IComparer) übergeben. Die Implementierung ist einfach; es muss nur eine Methode Compare(...) zur Verfügung gestellt werden. Diese sollte einen Wert kleiner 0 zurückliefern, wenn das erste Objekt kleiner ist als das zweite Objekt, 0 wenn diese gleich sind und einen Wert größer als 0, wenn das erste Objekt größer ist als das zweite. Die [i]ArrayList[i] Klasse implementiert die IList Schnittstelle, welche von IEnumerable abgeleitet ist. Jede Klasse, welche IEnumerable implementiert kann auch sehr einfach durchlaufen werden. Man muss sich nur merken, wie man dieses tut.
Code:
[i]// in C#[/i]
[b]foreach [/b]([b]object [/b]foo [b]in [/b]myArrayList) [b]do[/b] [i]// Do something with foo[/i]
Delphi-Quellcode:
System.Collections.Hashtable
// in Delphi 9 (ähnlich, allerdings muss Foo deklariert werden)
var Foo: System.Object; begin ... for Foo in MyArrayList do // Do something with Foo Ich liebe Hashtabellen und nutze diese überall und immer wieder. Die Implementierung dieser durch die FCL ist so flexibel gehalten, dass auch Du diese bald genauso nutzen wirst. Die Benutzung einer Hashtabelle ist sehr einfach:
Delphi-Quellcode:
Es gibt allerdings ein paar Dinge, welche berücksichtigt werden sollten. Erstens, wird ein Schlüssel/Wert auf nil gesetzt, so wird der Schlüssel nicht aus der Hashtabelle entfernt, dazu muss die Methode Remove(Schlüssel) aufgerufen werden. Will man alle Schlüssel/Werte-Paare durchlaufen, so kann man auf die Keys bzw. auf die Werteeigenschaften der Hashtabelle zugreifen:
var
MyHash: Hashtable; MyObj: System.Object; MyInt: Integer; begin MyHash := Hashtable.Create; MyHash['Key1'] := Self; { Overwrite the value } MyHash['Key1'] := 1; { Access the value } MyObj := MyHash['Key1']; { We know it is an integer } if MyObj <> nil then MyInt := Integer(MyObj); { Set the value to nil; 'Key1' still exists in the hashtable } MyHash['Key1'] := nil; { Remove it from the hashtable } MyHash.Remove('Key1'); end;
Delphi-Quellcode:
Will man auf alle Schlüssel/Werte-Paare zugreifen, so muss man die Schnittstelle IDictionaryEnumerator manuell ansprechen:
for Obj in MyHash.Keys do
Console.Write(Obj.ToString);
Delphi-Quellcode:
System.Diagnostics
var
MyHash: Hashtable; Enumerator: IDictionaryEnumerator; begin MyHash := Hashtable.Create; ... Enumerator := MyHash.GetEnumerator; while Enumerator.MoveNext do begin Console.WriteLine('Key: ' + Enumerator.Key.ToString); Console.WriteLine('Value: ' + Enumerator.Value.ToString); end; end; Oft will man sicherstellen, dass der geschriebene Code auch korrekt ist. Nutzt man den Namensraum System.Diagnostics kann man jederzeit auf die Klasse Debug und die statischen Variablen zugreifen um Asserts durchzuführen:
Delphi-Quellcode:
Wird ein Assert ausgeführt, so kann man in einem Debugger leicht erkennen, warum ein Assert eine Meldung ausgibt. Ich nutze diese Debuggingmethode ständig und finde diese unentbehrlich.
Debug.Assert(foo = bar, 'Foo should be equal to bar');
|
Re: [BorCon] Einführung in die .NET Framework Klassen Biblio
Zu guter Letzt Die FCL ist riesengroß. Ich könnte ein Buch schreiben, welche all die coolen Dinge beschreibt und was man mit ihnen tun kann. Vorläufig solltest Du das hier gezeigte nur als Spitze eine Eisberges betrachten. Lade Dir den SourceCode herunter und schaue Dir einige der hier genannten Beispiele an. |
Re: [BorCon] Einführung in die .NET Framework Klassen Biblio
Corbin, der diesen Artikel im Original verfasst hat, hat dazu auch, wie oben übersetzt, Source Codes geschrieben. Jetzt kommt aber der Hammer: er hat diese noch nicht auf der Borland CodeCentral veröffentlicht. Jedoch dürfen wir diese hier Euch schon vorab zum Download anbieten.
Sobald er seine Code aus der Borland CodeCentral veröffentlicht hat, werden wir dorthin verlinken und die Kopie hier entfernen. So, nun ist es auch schon soweit: ![]() Viel Spass, Euer sakura ...:cat:... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:27 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz