AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Tutorials Delphi [BorCon] Einführung in die .NET Framework Klassen Bibliothek
Tutorial durchsuchen
Ansicht
Themen-Optionen

[BorCon] Einführung in die .NET Framework Klassen Bibliothek

Ein Tutorial von sakura · begonnen am 11. Okt 2004 · letzter Beitrag vom 11. Okt 2004
Tutorial geschlossen
Benutzerbild von sakura
sakura
Registriert seit: 10. Jun 2002
Ich habe von Corbin Dunn die schriftliche Genehmigung seinen Artikel Introduction to the .NET Framework Class Library für die Delphi-PRAXiS ins Deutsche zu übersetzen. Folgend also die unkommentierte (und hoffentlich korrekte) Übersetzung. Wer Fragen zu diesem Artikel hat, möge diese bitte in einen eigenen Thread posten.

......
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#2
  Alt 11. Okt 2004, 13:44
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:
  • Erstellung extravaganter Benutzeroberflächen (GUI) (System.Windows.Forms)
  • Zugriff und Manipulation von Daten in verschiedenen Datenbanken (System.Data und System.Xml)
  • Dynamische Abfrage von Typen Informationen (System.Reflection)
  • Durchführung grundlegender Ein- und Ausgaberoutinen (System.IO)
  • Überprüfung der Rechte innerhalb des Betriebssystems (System.Security)
  • Erstellung von Internetanwendungen (System.Net und System.Net.Sockets)
  • Erstellung dynamischer internetbasierter Anwendungen - auch bekannt als ASP.NET (System.Web)
  • Zugriff auf allgemeine Datentypen, Ereignisshandler und Fehler (System)

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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#3
  Alt 11. Okt 2004, 13:46
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:
      Point firstPoint = [b]new[/b] Point(5, 5);
      Point secondPoint = firstPoint;
      firstPoint.X = 6;
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.

Betrachten wir nun folgenden C# Code:
Code:
      Control firstControl = [b]new[/b] Control();
      Control secondControl = firstControl;
      firstControl.BackColor = Color.Red;
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.

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:
      [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]
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.

Beachte, dass Delphi selbst als sehr typensicher gilt und betrachte den folgenden (zu oben analogen) Delphi.NET Quellcode:
Delphi-Quellcode:
var
  I: Integer;
  Obj: TObject;
begin
  I := 5;
  Obj := I; // Compiler error: Incompatible types
// I := Obj; // Expected compiler error
  I := Integer(Obj);
end.
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:
Obj := TObject(I); oder durch den Compilerschalter AUTOBOX, welcher irgenwo vor der "Problemzeile" erscheinen muss:
{$AUTOBOX ON} 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.

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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#4
  Alt 11. Okt 2004, 13:50
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:
[b]public delegate void [/b]SampleEventHandler([b]object [/b]sender, EventArgs args);
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.

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:
[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);
Wenn das Delegat aufgerufen wird, dann kann die aufgerufene Methode auf args.MySpecialValue zugreifen.

Einen eigenen Delegat in Delphi zu erstellen ist recht einfach und bekannt, einfach die bekannte Methodentypendeklaration nutzen:
Delphi-Quellcode:
type
  TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
Erstellen und Nutzen eines Delegates
In C# erstellt man ein Multicast-Delegat mit Hilfe des Schlüsselwortes event:
Code:
[b]public event [/b]SampleEventHandler EventTest;
Will man einem Ereignis einen (weiteren) Ereignishandler zufügen, so muss man in C# die += Syntax nutzen:
Code:
      [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!");
      }
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:
Code:
// 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);
  }
}
Beachte die Nutzung von Delegate.Combine und die implizite Übergabe des Wertetypen als Parameter.

Die allgemeine Art einen einfachen Ereignisshandler in Delphi zu schreiben sieht wie folgend aus:
Delphi-Quellcode:
type
  TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
 
  TWinForm18 = class(System.Windows.Forms.Form)
  private
    FSingleSampleEventHandler: TSampleEventHandler;
 
    property SampleEventHandler: TSampleEventHandler
      read FSingleSampleEventHandler write FSingleSampleEventHandler;
  end;
Um mehrfache (multi-cast) Ereignishandler zu schreiben muss man die Schlüsselwörter add und remove nutzen:
Delphi-Quellcode:
type
  TSampleEventHandler = procedure(Sender: TObject; Args: EventArgs);
 
  TWinForm18 = class(System.Windows.Forms.Form)
  private
    FMultiSampleEventHandler: TSampleEventHandler;
 
    property MultiSampleEventHandler: TSampleEventHandler
      add FMultiSampleEventHandler remove FMultiSampleEventHandler;
  end;
Anstelle der (neuen) Standardsyntax für Mehrfach-Ereignishandler (hier FMultiSampleEventHandler) kann man die Implementierung auch manuell gestallten:
Delphi-Quellcode:
    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;
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#.

In Delphi kann man weiterhin die Standard Art nutzen, um einen Ereignishandler zu setzen:
Delphi-Quellcode:
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
  e: System.EventArgs);
begin
  SampleEventHandler := OnSampleEvent;
end;
Will man jedoch auf mulit-casts nicht verzichten, so muss man Include(...) nutzen:
Delphi-Quellcode:
procedure TWinForm18.TWinForm18_Load(sender: System.Object;
  e: System.EventArgs);
begin
  Include(MultiSampleEventHandler, OnSampleEvent);
end;
Um einen Ereignishandler zu entfernen muss man Exclude(...) nutzen.

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:
// Delphi Syntax
  if Assigned(FSingleSampleEventHandler) then
    FSingleSampleEventHandler(Self, EventArgs.Create);
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"
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#5
  Alt 11. Okt 2004, 14:51
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:
[AttributeTypeName(Parameter, Parameter)]
[b]public class [/b]Foo { ... }
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.

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:
      [DesignerAttribute("MyControlDesigner")]
      [b]public class [/b]MyControl : System.Windows.Forms.Control
      { … }
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.

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:
      [ComVisible(true)]
      [b]public interface [/b]MyInterface
      {

      }
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:
Code:
[assembly: ComVisible(true)]
Assembly-Level Attribute können auch für andere Dinge genutzt werden (z.B.: Version, Autor, Firmenname, etc.):
Code:
[assembly: AssemblyTitle("This is my cool assembly")]
[assembly: AssemblyDescription("It does neat stuff")]
[assembly: AssemblyCompany("Borland Software Corporation")]
[assembly: AssemblyVersion("1.0.3.5")]
Attribute abfragen

Um Attribute für einen Typen oder eine Instanz zu ermitteln kann man sich der Reflection und des TypeDescriptor bedienen:
Delphi-Quellcode:
var
  Attributes: AttributeCollection;
begin
  Attributes := TypeDescriptor.GetAttributes(Self);
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.

Nachdem man Zugriff auf die AttributeCollection erlangt hat, kann man diese recht einfach durchlaufen:
Delphi-Quellcode:
for AnAttribute in Attributes do
  begin
    TextBox1.Text := TextBox1.Text + AnAttribute.ToString + #13#10;
  end;
Hinweis: Hier wurde die neuere "Delphi 9" for-Schleifen-Syntax genutzt. In Delphi 8 muss man den Iterator manuell erstellen und durchlaufen.

Wenn man nur auf ein bestimmtes Attribute zugreifen möchte, so kann man das wie folgend tun:
Delphi-Quellcode:
var
  Attributes: AttributeCollection;
  DefaultEventAttr: DefaultEventAttribute;
begin
...
  DefaultEventAttr :=
      DefaultEventAttribute(Attributes[TypeOf(DefaultEventAttribute)]);
  if DefaultEventAttr <> nil then
    Text := 'Has a Default Event name of: ' + DefaultEventAttr.Name;
Man beachte, dass null zurückgeliefert wird, wenn der Typ keine Attribute besitzt.

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:
[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;
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.

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:
  { This can only be applied to methods }
  [AttributeUsage(AttributeTargets.Method)]
  TMyCustomMethodAttriute = class(Attribute)

  end;
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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#6
  Alt 11. Okt 2004, 15:37
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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#7
  Alt 11. Okt 2004, 15:46
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:
Foo := Foo + 'Another String'; 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.

The Lösung ist denkbar einfach, man kann die Klasse System.Text.StringBuilder nutzen:
Delphi-Quellcode:
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;
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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#8
  Alt 11. Okt 2004, 16:19
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:
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;
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.

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:
// in Delphi 9 (ähnlich, allerdings muss Foo deklariert werden)
var
  Foo: System.Object;
begin
...
  for Foo in MyArrayList do
    // Do something with Foo
System.Collections.Hashtable

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:
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;
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:
Delphi-Quellcode:
  for Obj in MyHash.Keys do
    Console.Write(Obj.ToString);
Will man auf alle Schlüssel/Werte-Paare zugreifen, so muss man die Schnittstelle IDictionaryEnumerator manuell ansprechen:
Delphi-Quellcode:
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;
System.Diagnostics

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:

Debug.Assert(foo = bar, 'Foo should be equal to bar'); 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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#9
  Alt 11. Okt 2004, 16:23
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.
Daniel W.
 
Benutzerbild von sakura
sakura

 
Delphi 11 Alexandria
 
#10
  Alt 11. Okt 2004, 16:26
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: Hier findet Ihr den Source Code

Viel Spass,
Euer sakura

......
Daniel W.
 
Tutorial geschlossen


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:07 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz