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 19 Eigenschaften
  gp 19.1 Zugriffsroutinen
  gp 19.2 Eigenschaften und Vererbung
  gp 19.3 Verwendung von Eigenschaften
  gp 19.4 Nebeneffekte beim Setzen von Werten
  gp 19.5 Statische Eigenschaften
  gp 19.6 Eigenschafteneffizienz

Kapitel 19 Eigenschaften

Vor ein paar Monaten schrieb ich einen Codeabschnitt, in dem eines der Felder in einer Klasse (Fieldname) von einer anderen Klasse abgeleitet werden konnte (Name). Ich entschied daher, den Eigenschaftenausdruck (oder das Entwurfsmuster) von C++ zu verwenden und schrieb eine getFilename()-Funktion für das abgeleitete Feld. Anschließend musste ich den gesamten Code durchgehen und die Verweise auf das Feld durch Aufrufe von getFilename() ersetzen. Dies nahm einige Zeit in Anspruch, da es sich um ein recht großes Projekt handelte.

Darüber hinaus musste ich daran denken, beim Abrufen des Dateinamens die Mitgliedsfunktion getFilename() aufzurufen und nicht einfach auf die Dateinamensmitglieder der Klasse zu verweisen. Dies macht das Modell etwas weniger eingänglich; bei Filename handelte es sich nicht bloß um ein Feld, es war zu bedenken, dass beim Feldzugriff tatsächlich eine Funktion aufgerufen wurde.

In C# stellen Eigenschaften die Kernelemente der Sprache dar. Eigenschaften erscheinen dem Benutzer einer Klasse gegenüber als Felder, tatsächlich verwenden sie jedoch Mitgliedsfunktionen zum Abrufen des aktuellen Wertes bzw. zum Setzen eines neuen Wertes. Sie können das Benutzermodell (ein Feld) vom Implementierungsmodell (eine Mitgliedsfunktion) trennen. Auf diese Weise verringert sich der Kopplungsaufwand zwischen einer Klasse und dem Benutzer einer Klasse, und Sie bleiben bei Entwurf und Verwaltung flexibel.

In der .NET-Laufzeitumgebung werden Eigenschaften unter Zuhilfenahme eines Namensmusters und wenigen, zusätzlichen Metadaten implementiert, durch die Mitgliedsfunktionen und Eigenschaftenname miteinander verknüpft werden. Dies ermöglicht es einer Eigenschaft, in einigen Sprachen wie eine Eigenschaft und in anderen Sprachen wie eine Mitgliedsfunktion zu erscheinen.

Eigenschaften werden innerhalb der .NET-Basisklassenbibliothek sehr oft eingesetzt; tatsächlich gibt es nahezu keine öffentlichen Felder.


Galileo Computing

19.1 Zugriffsroutinen  downtop

Eine Eigenschaft setzt sich aus einer Eigenschaftendeklaration und entweder einem oder zwei Codeblöcken (den Zugriffsroutinen) zusammen, mit denen das Abrufen oder Festlegen der Eigenschaft gehandhabt wird. Hier ein einfaches Beispiel:

class Test
{
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
}

Diese Klasse deklariert eine Eigenschaft namens Name und definiert sowohl eine Festlegungs- als auch eine Abrufroutine für diese Eigenschaft. Die Abrufroutine gibt lediglich den Wert der privaten Variablen wieder, die Festlegungsroutine aktualisiert die interne Variable mit Hilfe eines speziellen Parameters namens value. Immer dann, wenn die Festlegungsroutine aufgerufen wird, enthält die Variable value den Wert, auf den die Eigenschaft gesetzt werden soll. Der Typ von value stimmt mit dem der Eigenschaft überein.

Eigenschaften können über eine Abrufroutine, eine Festlegungsroutine oder über beides verfügen. Eine Eigenschaft, die lediglich über eine Abrufroutine verfügt, wird als schreibgeschützte Eigenschaft bezeichnet; eine Eigenschaft, die nur über eine Festlegungsroutine verfügt, wird lesegeschützte Eigenschaft genannt.


Galileo Computing

19.2 Eigenschaften und Vererbung  downtop

Wie Mitgliedsfunktionen können auch Eigenschaften mit den Modifikatoren virtual, override oder abstract deklariert werden. Diese Modifikatoren werden anstelle der Eigenschaft gesetzt und wirken sich auf beide Zugriffsroutinen aus.

Wenn eine abgeleitete Klasse eine Eigenschaft deklariert, deren Namen mit der Basisklasse übereinstimmt, wird die gesamte Eigenschaft verborgen. Es ist nicht möglich, nur die Festlegungs- oder nur die Abrufroutine auszublenden.


Galileo Computing

19.3 Verwendung von Eigenschaften  downtop

Eigenschaften trennen die Schnittstelle einer Klasse von der Implementierung einer Klasse. Dies ist dann nützlich, wenn die Eigenschaft von anderen Feldern abgeleitet wird oder in Fällen, in denen eine verzögerte Initialisierung erfolgt und ein Wert nur abgerufen wird, wenn der Benutzer ihn tatsächlich benötigt.

Angenommen, ein Automobilhersteller möchte einen Bericht erzeugen, in dem aktuelle Informationen zur Fahrzeugherstellung aufgelistet werden.

using System;
class Auto
{
    public Auto(int id, string name)
    {
        this.id = id;
        this.name = name;
    }

        // Abfrage zum Auffinden der Produktionszahl #
    public int ProductionCount
    {
        get
        {
            if (productionCount == -1)
            {
                // Anzahl aus Datenbank hier abrufen
            }
            return(productionCount);
        }
    }
    public int SalesCount
    {
        get
        {
            if (salesCount == -1)
            {
                // Daten von jedem Händler abfragen
            }
            return(salesCount);
        }
    }
    string name;
    int id;
    int productionCount = -1;
    int salesCount = -1;
}

Die Eigenschaften ProductionCount und SalesCount werden mit dem Wert -1 initialisiert, die aufwendige Berechnung der Werte wird erst durchgeführt, wenn dies tatsächlich erforderlich ist.


Galileo Computing

19.4 Nebeneffekte beim Setzen von Werten  downtop

Eigenschaften eignen sich zu mehr als nur zum Setzen von Werten beim Aufruf der Festlegungsroutine. Ein Einkaufskorb könnte beispielsweise die Gesamtsumme aktualisieren, wenn der Benutzer die Artikelanzahl im Einkaufskorb ändert.

using System;
using System.Collections;
class Basket
{
    internal void UpdateTotal()
    {
        total = 0;
        foreach (BasketItem item in items)
        {
            total += item.Total;
        }
    }

    ArrayList    items = new ArrayList();
    Decimal    total;
}
class BasketItem
{
    BasketItem(Basket basket)
    {
        this.basket = basket;
    }
    public int Quantity
    {
        get
        {
            return(quantity);
        }
        set
        {
            quantity = value;
            basket.UpdateTotal();
        }
    }
    public Decimal Price
    {
        get
        {
            return(price);
        }
        set
        {
            price = value;
            basket.UpdateTotal();
        }
    }
    public Decimal Total
    {
        get
        {
                // Mengenrabatt; 10%, wenn mehr als 10 Artikel 
bestellt werden
            if (quantity >= 10)?
                return(quantity * price * 0.90m);
            else
                return(quantity * price);
        }
    }

    int        quantity;     // Anzahl Artikel
    Decimal    price;        // Artikelpreis
    Basket     basket;       // Rückverweis auf Einkaufskorb
}

In diesem Beispiel enthält die Klasse Basket ein Array aus Korbartikeln (BasketItem). Wenn Preis oder Menge eines Artikels aktualisiert werden, wird eine Aktualisierung an die Basket-Klasse zurückgegeben, und es werden alle Artikel durchlaufen, um die Gesamtsumme für den Einkaufskorb zu aktualisieren.

Diese Interaktion könnte mit Hilfe von Ereignissen auf allgemeinere Weise implementiert werden, siehe hierzu Kapitel 23, Ereignisse.


Galileo Computing

19.5 Statische Eigenschaften  downtop

Neben den Mitgliedseigenschaften ermöglicht C# außerdem die Definition statischer Eigenschaften. Diese werden nicht nur auf eine spezifische Klasseninstanz, sondern auf die gesamte Klasse angewendet. Wie die statischen Mitgliedsfunktionen können auch Eigenschaften nicht mit den Modifikatoren virtual, override oder abstract deklariert werden.

Bei der Erläuterung der schreibgeschützten Felder in Kapitel 8, Mehr zu Klassen, wurde ein Fall vorgestellt, in dem einige statische readonly-Felder initialisiert wurden. Dies ist auch mit statischen Eigenschaften möglich; die Felder können erst bei Bedarf initialisiert werden. Der Wert kann ebenfalls erst dann berechnet werden, wenn es erforderlich ist, und muss nicht gespeichert werden. Wenn das Erstellen von Feldern ressourcenintensiv ist und das Feld wahrscheinlich später wieder eingesetzt wird, sollte der Wert in einem privaten Feld zwischengespeichert werden. Erfordert die Felderstellung nur geringe Ressourcen oder wird das Feld wahrscheinlich nicht mehr benötigt, kann dieses nach Bedarf erstellt werden.

class Color
{
    public Color(int red, int green, int blue)
    {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    int    red;
    int    green;
    int    blue;

    public static Color Red
    {
        get
        {
            return(new Color(255, 0, 0));
        }
    }
    public static Color Green
    {
        get
        {
            return(new Color(0, 255, 0));
        }
    }
    public static Color Blue
    {
        get
        {
            return(new Color(0, 0, 255));
        }
    }
}
class Test
{
    static void Main()
    {
        Color background = Color.Red;
    }
}

Möchte der Benutzer einige der vordefinierten Farbwerte verwenden, wird über die Abrufroutine in der Eigenschaft aus dem Stegreif eine Instanz mit der geeigneten Farbe erstellt, anschließend wird die Instanz zurückgegeben.


Galileo Computing

19.6 Eigenschafteneffizienz  toptop

Zurückgehend auf das erste Beispiel in diesem Kapitel untersuchen wir nun die Effizienz des Codes bei dessen Ausführung:

class Test
{
    private string    name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
        }
    }
}

Dieser Entwurf mag ein wenig ineffizient erscheinen, da sich an einer Stelle ein Mitgliedsfunktionsaufruf befindet, wo ein Feldzugriff erwartet würde. Es liegt jedoch kein Grund vor, weshalb die zugrunde liegende Laufzeitumgebung für die Zugriffsroutinen nicht wie für jede andere einfache Funktion ein Inlining durchführen könnte, daher ergibt sich bei Auswahl einer Eigenschaft statt eines Feldes häufig keine Leistungseinbuße. Die Möglichkeit, eine Implementierung zu einem späteren Zeitpunkt bearbeiten zu können, ohne die Schnittstelle zu ändern, kann von unschätzbarem Vorteil sein, daher stellen Eigenschaften gegenüber Feldern für öffentliche Mitglieder normalerweise die bessere Wahl dar.

Es bleibt jedoch ein kleiner Nachteil bei der Verwendung von Eigenschaften: Sie werden nicht von allen .NET-Sprachen als systemeigen unterstützt, daher müssen andere Sprachen möglicherweise direkt auf die Zugriffsfunktionen zugreifen, was ein wenig komplizierter ist als die Verwendung von Feldern.






1    Die Win32-Version der .NET-Laufzeitumgebung führt ein Inlining für einfache Zugriffsroutinen aus, auch wenn dies bei anderen Umgebungen nicht erforderlich ist.

   

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