Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 23 Zuweisungen
  gp 22.4 Attributreflektion
  gp 23.1 Verwenden von Zuweisungen
  gp 23.2 Zuweisungen als statische Mitglieder
  gp 23.3 Zuweisungen als statische Eigenschaften


Galileo Computing

22.4 Attributreflektion  downtop

Nachdem Attribute im Code definiert wurden, ist es sinnvoll, die Attributwerte ermitteln zu können. Dies wird mit Hilfe der Reflektion erreicht.

Der folgende Code zeigt eine Attributklasse, die Anwendung des Attributs auf eine Klasse sowie die Reflektion einer Klasse zum Abrufen der Attribute.

using System;
using System.Reflection;
[AttributeUsage(AttributeTargets.Class)]
public class CodeReviewAttribute: System.Attribute
{
    public CodeReviewAttribute(string reviewer, string date)
    {
        this.reviewer = reviewer;
        this.date = date;
    }
    public string Comment
    {
        get
        {
            return(comment);
        }
        set
        {
            comment = value;
        }
    }
    public string Date
    {
        get
        {
            return(date);
        }
    }
    public string Reviewer
    {
        get
        {
            return(reviewer);
        }
    }
    string reviewer;
    string date;
    string comment;
}
[CodeReview("Eric", "01-12-2000", Comment="Bitchin' Code")]
class Complex
{
}

class Test
{
    public static void Main()
    {
        System.Reflection.MemberInfo info;
        info = typeof(Complex);
        object[] atts;
        atts = info.GetCustomAttributes(typeof(CodeReviewAttribute));
        if (atts.GetLength(0) != 0)
        {
            CodeReviewAttribute    att = (CodeReviewAttribute) atts[0];
            Console.WriteLine("Reviewer: {0}", att.Reviewer);
            Console.WriteLine("Date: {0}", att.Date);
            Console.WriteLine("Comment: {0}", att.Comment);
        }
    }
}

Die Main()-Funktion ruft zunächst den mit Complex verknüpften Objekttyp ab. Anschließend werden alle Attribute geladen, die den Typ CodeReviewAttribute aufweisen. Verfügt das Attributarray über Einträge, wird für das erste Element ein cast in ein CodeReviewAttribute durchgeführt, und der Wert wird ausgegeben. Es kann sich nur ein Eintrag im Array befinden, da CodeReviewAttribute nur einmalig verwendet werden kann.

Dieser Code erzeugt die folgende Ausgabe:

Reviewer: Eric
Date: 01-12-2000
Comment: Bitchin' Code

GetCustomAttribute() kann auch ohne Typ aufgerufen werden, um alle benutzerdefinierten Attribute für dieses Objekt zu erhalten.


Kapitel 23 Zuweisungen

Zuweisungen (delegate-Schlüsselwort) sind Schnittstellen dahingehend ähnlich, dass sie Festlegungen zwischen einer aufrufenden Komponente und einer implementierenden Komponente treffen. Statt jedoch eine ganze Schnittstelle zu spezifizieren, wird über eine Zuweisung nur die Form einer einzelnen Funktion festgelegt. Darüber hinaus werden Schnittstellen zur Kompilierungszeit, Zuweisungen dagegen zur Laufzeit erstellt.


Galileo Computing

23.1 Verwenden von Zuweisungen  downtop

Die Spezifikation der Zuweisung (delegate) bestimmt die Form der Funktion, und zum Erstellen einer Zuweisungsinstanz muss eine Funktion vorliegen, die dieser Form entspricht. Zuweisungen werden gelegentlich auch als »sichere Funktionszeiger« bezeichnet. Im Gegensatz zu Funktionszeigern können C#-Zuweisungen jedoch mehr als eine Funktion aufrufen; werden zwei Zuweisungen zusammengefügt, ergibt sich so eine Zuweisung, mit der beide Zuweisungen aufgerufen werden.

Aufgrund ihrer dynamischeren Natur sind Zuweisungen immer dort nützlich, wo der Benutzer das Verhalten ändern möchte. Wenn beispielsweise eine Auflistungsklasse eine Sortierfunktion implementiert, sollen vielleicht verschiedene Sortierreihenfolgen unterstützt werden. Die Sortierung kann basierend auf einer Zuweisung gesteuert werden, mit der die Vergleichsfunktion definiert wird.

using System;
public class Container
{
    public delegate int CompareItemsCallback(object obj1, object obj2);
    public void Sort(CompareItemsCallback compare)
    {
        // keine richtige Sortierung, nur eine Demonstration dessen,
        // was der innere Schleifencode tun kann
        int x = 0;
        int y = 1;
        object    item1 = arr[x];
        object item2 = arr[y];
        int order = compare(item1, item2);
    }
    object[]    arr = new object[1];    // Elemente in der Auflistung
}
public class Employee
{
    Employee(string name, int id)
    {
        this.name = name;
        this.id = id;
    }
    public static int CompareName(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;
        return(String.Compare(emp1.name, emp2.name));
    }
    public static int CompareId(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;

        if (emp1.id > emp2.id)
            return(1);
        if (emp1.id < emp2.id)
            return(-1);
        else
            return(0);
    }
    string    name;
    int    id;
}
class Test
{
    public static void Main()
    {
        Container employees = new Container();
        // Hier einige Mitarbeiter erstellen und hinzufügen

            // Zuweisung zur Sortierung nach Namen erstellen und Sortierung 
durchführen
        Container.CompareItemsCallback sortByName =
            new Container.CompareItemsCallback(Employee.CompareName);
        employees.Sort(sortByName);
            // Mitarbeiter sind jetzt nach Name sortiert
    }
}

Die in der Container-Klasse definierte Zuweisung (delegate) speichert die zwei zu vergleichenden Objekte als Parameter und gibt einen integer-Wert zurück, der die Reihenfolge der zwei Objekte angibt. Es werden zwei statische Funktionen deklariert, die als Bestandteil der Employee-Klasse mit dieser Zuweisung übereinstimmen (bei allen Zuweisungen muss es sich um statische Funktionen handeln). Hierbei wird durch jede Funktion eine andere Sortierung beschrieben.

Wenn der Container sortiert werden soll, kann eine Zuweisung übergeben werden, in der die zu verwendende Sortierung beschrieben wird. Anschließend wird die Sortierung über die Sortierfunktion durchgeführt.

Nun, das geschähe, wenn diese tatsächlich implementiert wäre.


Galileo Computing

23.2 Zuweisungen als statische Mitglieder  downtop

Ein Nachteil bei diesem Ansatz ist der, dass der Benutzer, der die Sortierung verwenden möchte, eine Zuweisungsinstanz mit der geeigneten Funktion erstellen muss. Es wäre schöner, wenn dies nicht erforderlich wäre – erreicht wird dies, indem Sie die geeigneten Zuweisungen als statische Mitglieder von Employee definieren:

using System;
public class Container
{
    public delegate int CompareItemsCallback(object obj1, object obj2);
    public void Sort(CompareItemsCallback compare)
    {
        // keine richtige Sortierung, nur eine Demonstration dessen,
        // was der innere Schleifencode tun kann
        int x = 0;
        int y = 1;
        object    item1 = arr[x];
        object item2 = arr[y];
        int order = compare(item1, item2);
    }
    object[]    arr = new object[1];    // Elemente in der Auflistung
}
class Employee
{
    Employee(string name, int id)
    {
        this.name = name;
        this.id = id;
    }
    public static readonly Container.CompareItemsCallback SortByName 
=
        new Container.CompareItemsCallback(CompareName);
    public static readonly Container.CompareItemsCallback SortById =
        new Container.CompareItemsCallback(CompareId);

    public static int CompareName(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;
        return(String.Compare(emp1.name, emp2.name));
    }
    public static int CompareId(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;

        if (emp1.id > emp2.id)
            return(1);
        if (emp1.id < emp2.id)
            return(-1);
        else
            return(0);
    }
    string    name;
    int    id;
}
class Test
{
    public static void Main()
    {
        Container employees = new Container();
        // Hier einige Mitarbeiter erstellen und hinzufügen

        employees.Sort(Employee.SortByName);
            // Mitarbeiter sind jetzt nach Name sortiert
    }
}

Diese Vorgehensweise ist sehr viel einfacher. Die Benutzer von Employee müssen nicht wissen, wie die Zuweisung erstellt wird – sie können einfach auf das statische Mitglied verweisen.


Galileo Computing

23.3 Zuweisungen als statische Eigenschaften  toptop

Ein weiterer unschöner Punkt ist der, dass die Zuweisung immer erstellt wird, selbst dann, wenn sie nie eingesetzt wird. Das ist unnötig. Es wäre besser, wenn die Zuweisung nach Bedarf erstellt würde. Dies kann erreicht werden, indem die statischen Funktionen durch Eigenschaften ersetzt werden:

using System;
class Container
{
    public delegate int CompareItemsCallback(object obj1, object obj2);
    public void SortItems(CompareItemsCallback compare)
    {
        // keine richtige Sortierung, nur eine Demonstration dessen,
        // was der innere Schleifencode tun kann
        int x = 0;
        int y = 1;
        object    item1 = arr[x];
        object item2 = arr[y];
        int order = compare(item1, item2);
    }
    object[]    arr;    // Elemente in der Auflistung
}
class Employee
{
    Employee(string name, int id)
    {
        this.name = name;
        this.id = id;
    }
    public static Container.CompareItemsCallback SortByName
    {
        get
        {
            return(new Container.CompareItemsCallback(CompareName));
        }
    }
    public static Container.CompareItemsCallback SortById
    {
        get
        {
            return(new Container.CompareItemsCallback(CompareId));
        }
    }
    static int CompareName(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;
        return(String.Compare(emp1.name, emp2.name));
    }
    static int CompareId(object obj1, object obj2)
    {
        Employee emp1 = (Employee) obj1;
        Employee emp2 = (Employee) obj2;

        if (emp1.id > emp2.id)
            return(1);
        if (emp1.id < emp2.id)
            return(-1);
        else
            return(0);
    }
    string    name;
    int    id;
}
class Test
{
    public static void Main()
    {
        Container employees = new Container();
        // Hier einige Mitarbeiter erstellen und hinzufügen

        employees.SortItems(Employee.SortByName);
            // Mitarbeiter sind jetzt nach Name sortiert
    }
}

Bei dieser Version handelt es sich bei Employee.SortByName nicht um eine Zuweisung, sondern um eine Funktion, die eine Zuweisung zurückgibt, mit der eine Sortierung nach Name durchgeführt werden kann.

Anfänglich verfügte dieses Beispiel über die als privat deklarierten statischen Zuweisungsmitglieder SortByName und SortById, und das statische Mitglied wurde über die Eigenschaft erstellt, wenn es zuvor nicht benötigt worden war. Dies würde gut funktionieren, wenn die Zuweisungserstellung ressourcenintensiv und eine erneute Verwendung wahrscheinlich wäre.

In diesem Fall jedoch ist es sehr viel einfacher, die Zuweisung nach Bedarf zu erstellen und einfach an den Benutzer zurückzugeben. Sobald die Sort-Funktion über die Zuweisung für Container ausgeführt wurde, kann eine Speicherbereinigung erfolgen.


 

   

Select * from SQL Server 2000




Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de