 |
| |
|
|
 |
Autor |
Nachricht |
 |
| |
|
|
| |
|
|
| |
| sakura |
#3| Verfasst am: 11.10.2004, 13:46 Titel: Re: Einführung in die .NET Framework Klassen Bibliothek |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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: | markieren | Point firstPoint = new 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: | markieren | Control firstControl = new 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: | markieren | object obj = null; // a reference type
int i = 5; // a value type
obj = i; // boxing
//i = obj; // compiler error
i = (int)obj; // unboxing: the value type is copied to 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: | markieren | 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:
oder durch den Compilerschalter AUTOBOX, welcher irgenwo vor der "Problemzeile" erscheinen muss:
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. |
Zuletzt bearbeitet von sakura am 11.10.2004, 13:51, insgesamt 1-mal bearbeitet. |
 |
|
|
|
| |
| sakura |
#4| Verfasst am: 11.10.2004, 13:50 Titel: Re: Einführung in die .NET Framework Klassen Bibliothek |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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: | markieren | | public delegate void SampleEventHandler(object 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: | markieren | public class MyEventArgs : EventArgs
{
public MyEventArgs(string mySpecialValue)
{
MySpecialValue = mySpecialValue;
}
public string MySpecialValue;
}
public delegate void SampleSpecialEventHandler(object 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:
Erstellen und Nutzen eines Delegates
In C# erstellt man ein Multicast-Delegat mit Hilfe des Schlüsselwortes event:
Will man einem Ereignis einen (weiteren) Ereignishandler zufügen, so muss man in C# die += Syntax nutzen:
| Code: | markieren | private void WinForm_Load(object sender, System.EventArgs e)
{
EventTest += new SampleEventHandler(OnMyEvent);
}
private void OnMyEvent(object 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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · · · 15 16
| // Example of how to "roll out" an event handler
private SampleEventHandler otherEvent;
private event SampleEventHandler AnotherEventTest
{
add
{
otherEvent =
(SampleEventHandler)Delegate.Combine(otherEvent, value);
}
remove
{
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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10
| 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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10
| 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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · · · 15 · · · · 20 21
| 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: | markieren | 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: | markieren | 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: | markieren | // C# Syntax
if (EventTest != null)
EventTest(this, new EventArgs()); |
|
| Delphi-Quellcode: | markieren | // 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" |
Zuletzt bearbeitet von sakura am 11.10.2004, 14:20, insgesamt 1-mal bearbeitet. |
 |
|
|
|
| |
| sakura |
#5| Verfasst am: 11.10.2004, 14:51 Titel: Re: Einführung in die .NET Framework Klassen Bibliothek |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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: | markieren | [AttributeTypeName(Parameter, Parameter)]
public class 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: | markieren | [DesignerAttribute("MyControlDesigner")]
public class MyControl : System.Windows.Forms.Control
{ … } |
|
Wenn der WinForms Designer einen Designer für ein Control erstellt, stößt dieser auf das DesignerAttribute[i] der Klasse und erstellt einen [i]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: | markieren | [ComVisible(true)]
public interface 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:
Assembly-Level Attribute können auch für andere Dinge genutzt werden (z.B.: Version, Autor, Firmenname, etc.):
| Code: | markieren | [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:
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: | markieren | 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: | markieren | 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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · · · 15 · · · · 20
| [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: | markieren | { 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. |
Zuletzt bearbeitet von sakura am 11.10.2004, 14:52, insgesamt 1-mal bearbeitet. |
 |
|
|
|
| |
| sakura |
#6| Verfasst am: 11.10.2004, 15:37 Titel: Re: Einführung in die .NET Framework Klassen Bibliothek |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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. |
|
 |
|
|
|
| |
| sakura |
#7| Verfasst am: 11.10.2004, 15:46 Titel: Re: [BorCon] Einführung in die .NET Framework Klassen Biblio |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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:
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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · · · 15 · · · · 20
| 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. |
|
 |
|
|
|
| |
| sakura |
#8| Verfasst am: 11.10.2004, 16:19 Titel: Re: [BorCon] Einführung in die .NET Framework Klassen Biblio |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · 13
| 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 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 [i]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 ArrayList[i] Klasse implementiert die [i]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: | markieren | // in C#
foreach (object foo in myArrayList) do
// Do something with foo |
|
| Delphi-Quellcode: | markieren | // 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: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · · · 15 · · · 19
| 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:
Will man auf alle Schlüssel/Werte-Paare zugreifen, so muss man die Schnittstelle IDictionaryEnumerator manuell ansprechen:
Delphi-Quellcode: | zusammenfalten | markieren | 1 · · · 5 · · · · 10 · · 13
| 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:
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. |
|
 |
|
|
|
| |
|
|
| |
| sakura |
#10| Verfasst am: 11.10.2004, 16:26 Titel: Re: [BorCon] Einführung in die .NET Framework Klassen Biblio |
 |
 |
 |
|
Super-Moderator Alter: 33 Status: offline Beiträge: 11.294 angemeldet: 10.06.2002 Wohnort: München RAD-Studio 2007 Architect

|
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
... ... |
 Jeder Mensch lebt praktisch in seiner eigenen, privaten Welt. Kommunikation bedeutet zwischen diesen Welten Brücken zu bauen.
CodeGear Associate Evangelist
Zuletzt bearbeitet von sakura am 11.10.2004, 19:13, insgesamt 1-mal bearbeitet. |
 |
|
|
|
 |
|
 |
| |
|
|
| |
 
|
|
| |
|
Du darfst keine Beiträge in dieses Forum schreiben. Du darfst auf Beiträge in diesem Forum nicht antworten. Du darfst Deine Beiträge in diesem Forum nicht bearbeiten. Du darfst Deine Beiträge in diesem Forum nicht löschen. Du darfst an Umfragen in diesem Forum nicht mitmachen. Du kannst Dateien in diesem Forum nicht posten. Du kannst Dateien in diesem Forum nicht herunterladen.
|
|
 |