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 20 Indizierer
  gp 20.1 Indizierung mit einem integer-Index
  gp 20.2 Indizierer und foreach
  gp 20.3 Entwurfsrichtlinien

Kapitel 20 Indizierer

Gelegentlich ist es hilfreich, ein Objekt so indizieren zu können, als handele es sich um ein Objekt. Dies kann erreicht werden, indem ein Indizierer für das Objekt geschrieben wird. Diesen kann man sich wie ein »schlaues« Array vorstellen. So wie eine Eigenschaft wie ein Feld aussieht, jedoch über Zugriffsroutinen zur Durchführung von get- und set-Operationen verfügt, sieht ein Indizierer aus wie ein Array, verfügt jedoch über Zugriffsroutinen zur Durchführung von Arrayindizierungsoperationen.


Galileo Computing

20.1 Indizierung mit einem integer-Index  downtop

Eine Klasse, die eine Datenbankzeile enthält, kann einen Indizierer für den Zugriff auf die Spalten in der Zeile implementieren:

using System;
using System.Collections;
class DataValue
{
    public DataValue(string name, object data)
    {
        this.name = name;
        this.data = data;
    }
    public string Name
    {
        get
        {
            return(name);
        }
        set
        {
            name = value;
        }
    }
    public object Data
    {
        get
        {
            return(data);
        }
        set
        {
            data = value;
        }
    }
    string    name;
    object data;
}
class DataRow
{
    public DataRow()
    {
        row = new ArrayList();
    }

    public void Load()
    {
        /* Hier Code laden */
        row.Add(new DataValue("Id", 5551212));
        row.Add(new DataValue("Name", "Fred"));
        row.Add(new DataValue("Salary", 2355.23m));
    }

    public object this[int column]
    {
        get
        {
            return(row[column – 1]);
        }
        set
        {
            row[column – 1] = value;
        }
    }
    ArrayList    row;
}
class Test
{
    public static void Main()
    {
        DataRow row = new DataRow();
        row.Load();
        DataValue    val = (DataValue) row[0];
        Console.WriteLine("Column 0: {0}", val.Data);
        val.Data = 12;    // ID setzen
    }
}

Die DataRow-Klasse weist Funktionen zum Laden einer Datenzeile und zum Speichern der Daten sowie eine Indizierungsfunktion für den Datenzugriff auf. In einer richtigen Klasse würden die Daten einer Datenbank über die Load()-Funktion geladen.

Die Indizierungsfunktion ist so wie eine Eigenschaft geschrieben, abgesehen davon, dass ein Indizierungsparameter verwendet wird. Der Indizierer wird mit Hilfe des Namens this deklariert, da kein Name für den Indizierer vorhanden ist.

Eine Klasse kann mehrere Indizierer aufweisen. Für die DataRow-Klasse kann es von Nutzen sein, den Namen der Spalte für die Indizierung einsetzen zu können:

using System;
using System.Collections;
class DataValue
{
    public DataValue(string name, object data)
    {
        this.name = name;
        this.data = data;
    }
    public string Name
    {
        get
        {
            return(name);
        }
        set
        {
            name = value;
        }
    }
    public object Data
    {
        get
        {
            return(data);
        }
        set
        {
            data = value;
        }
    }
    string    name;
    object data;
}
class DataRow
{
    public DataRow()
    {
        row = new ArrayList();
    }

    public void Load()
    {
        /* Hier Code laden */
        row.Add(new DataValue("Id", 5551212));
        row.Add(new DataValue("Name", "Fred"));
        row.Add(new DataValue("Salary", 2355.23m));
    }

    public object this[int column]
    {
        get
        {
            return(row[column – 1]);
        }
        set
        {
            row[column – 1] = value;
        }
    }
    int FindColumn(string name)
    {
        for (int index = 0; index < row.Count; index++)
        {
            DataValue dataValue = (DataValue) row[index];
            if (dataValue.Name == name)
                return(index);
        }
        return(-1);
    }
    public object this[string name]
    {
        get
        {
            return this[FindColumn(name)];
        }
        set
        {
            this[FindColumn(name)] = value;
        }
    }
    ArrayList    row;
}
class Test
{
    public static void Main()
    {
        DataRow row = new DataRow();
        row.Load();
        DataValue val = (DataValue) row["Id"];
        Console.WriteLine("Id: {0}", val.Data);
        Console.WriteLine("Salary: {0}",
        ((DataValue) row["Salary"]).Data);
        ((DataValue)row["Name"]).Data = "Barney";    // Name setzen
        Console.WriteLine("Name: {0}", ((DataValue) row["Name"]).Data);
    }
}

Der Zeichenfolgenindizierer verwendet die Funktion FindColum() zum Auffinden des Namensindex, anschließend wird der Indizierer int zur Durchführung der geeigneten Operation eingesetzt.

Indizierer können mehr als einen Parameter aufweisen, um ein mehrdimensionales virtuelles Array simulieren zu können.


Galileo Computing

20.2 Indizierer und foreach  downtop

Wenn ein Objekt wie ein Array behandelt werden kann, ist es oft hilfreich, dieses mit der foreach-Anweisung zu durchlaufen. Zur Verwendung von foreach und ähnlicher Konstruktionen in anderen .NET-Sprachen muss das Objekt die Schnittstelle IEnumerable implementieren. Diese Schnittstelle verfügt über ein einziges Mitglied namens GetEnumerator(), mit dem ein Verweis auf eine IEnumerable-Schnittstelle zurückgegeben wird, die wiederum Mitgliedsfunktionen zur Durchführung einer Auflistung besitzt.

Die Schnittstelle IEnumerable kann direkt durch die Containerklasse oder über eine private Klasse implementiert werden. Die private Implementierung ist hierbei vorzuziehen, da so die Auflistungsklasse vereinfacht wird.

Nachfolgend wird das vorstehende Beispiel um die Verwendung von foreach erweitert:

using System;
using System.Collections;
class DataValue
{
    public DataValue(string name, object data)
    {
        this.name = name;
        this.data = data;
    }
    public string Name
    {
        get
        {
            return(name);
        }
        set
        {
            name = value;
        }
    }
    public object Data
    {
        get
        {
            return(data);
        }
        set
        {
            data = value;
        }
    }
    string    name;
    object data;
}
class DataRow: IEnumerable
{
    class DataRowEnumerator: IEnumerator
    {
        public DataRowEnumerator(DataRow dataRow)
        {
            this.dataRow = dataRow;
            index = -1;
        }
        public bool MoveNext()
        {
            index++;
            if (index >= dataRow.row.Count)
                return(false);
            else
                return(true);
        }
        public void Reset()
        {
            index = -1;
        }
        public object Current
        {
            get
            {
                return(dataRow.row[index]);
            }
        }
        DataRow    dataRow;
        int        index;
    }
    public DataRow()
    {
        row = new ArrayList();
    }

    public void Load()
    {
        /* Hier Code laden */
        row.Add(new DataValue("Id", 5551212));
        row.Add(new DataValue("Name", "Fred"));
        row.Add(new DataValue("Salary", 2355.23m));
    }

    public object this[int column]
    {
        get
        {
            return(row[column – 1]);
        }
        set
        {
            row[column – 1] = value;
        }
    }
    int FindColumn(string name)
    {
        for (int index = 0; index < row.Count; index++)
        {
            DataValue dataValue = (DataValue) row[index];
            if (dataValue.Name == name)
                return(index);
        }
        return(-1);
    }
    public object this[string name]
    {
        get
        {
            return this[FindColumn(name)];
        }
        set
        {
            this[FindColumn(name)] = value;
        }
    }
    public IEnumerator GetEnumerator()
    {
        return((IEnumerator) new DataRowEnumerator(this));
    }
    ArrayList    row;
}
class Test
{
    public static void Main()
    {
        DataRow row = new DataRow();
        row.Load();
        foreach (DataValue dataValue in row)
        {
            Console.WriteLine("{0}: {1}",
                dataValue.Name, dataValue.Data);
        }
    }
}
Die foreach-Schleife in Main() wird vom Compiler folgendermaßen 
umgeschrieben:
IEnumerator enumerator = row.GetEnumerator();
while (enumerator.GetNext())
{
    DataValue dataValue =
        (DataValue) enumerator.Current;
    Console.WriteLine("{0}: {1}",
        dataValue.Name, dataValue.Data)
}

Galileo Computing

20.3 Entwurfsrichtlinien  toptop

Indizierer sollten nur in Situationen eingesetzt werden, in denen die Abstraktion Sinn macht. Dies hängt normalerweise davon ab, ob das Objekt als Container für ein anderes Objekt fungiert.

Indizierer sollten sowohl über eine Abruf- als auch eine Festlegungsroutine verfügen, genau wie Arrays Objekte lesen und in diesen schreiben können. Wenn der Indizierer lediglich über eine Festlegungsroutine verfügt, sollten Sie diesen eventuell durch eine Methode ersetzen.

   

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