.NET Tip #16: IEnumerator aneb procházíme všechno na světě

Tomáš Herceg       15. 10. 2008       .NET Tips       7155 zobrazení

Pokud pracujete s poli nebo kolekcemi, jistě jste někdy použili cyklus For Each. Velmi jednoduše pomocí něj můžeme projít pole hodnot nebo třeba kolekci objektů. Celý tento cyklus funguje díky mechanismu enumerátorů.

Enumerátor je speciální třída implementující rozhraní IEnumerator, která umí procházet položky nějaké datové struktury. V tomto .NET tipu si ukážeme, jak můžeme takový enumerátor sami napsat.

Napíšeme si třídu NumberSequence, které v konstruktoru předáme tři čísla – počíteční hodnotu, koncovou hodnotu a krok, po němž se má pohybovat.

''' <summary>
''' Sekvence čísel
''' </summary>
Public Class NumberSequence

    Private _startValue As Integer
    ''' <summary>
    ''' Počáteční hodnota
    ''' </summary>
    Public Property StartValue() As Integer
        Get
            Return _startValue
        End Get
        Set(ByVal value As Integer)
            _startValue = value
        End Set
    End Property

    Private _endValue As Integer
    ''' <summary>
    ''' Koncová hodnota
    ''' </summary>
    Public Property EndValue() As Integer
        Get
            Return _endValue
        End Get
        Set(ByVal value As Integer)
            _endValue = value
        End Set
    End Property

    Private _increment As Integer
    ''' <summary>
    ''' Přírůstek po každém kroku
    ''' </summary>
    Public Property Increment() As Integer
        Get
            Return _increment
        End Get
        Set(ByVal value As Integer)
            _increment = value
        End Set
    End Property

    ''' <summary>
    ''' Vytvoří novou instanci třídy NumberSequence
    ''' </summary>
    Public Sub New(ByVal _startValue As Integer, ByVal _endValue As Integer, ByVal _increment As Integer)
        Me.StartValue = _startValue
        Me.EndValue = _endValue
        Me.Increment = _increment
    End Sub

End Class

Nyní si napíšeme náš enumerátor, je to třída implementující rozhraní IEnumerator.

''' <summary>
''' Enumerátor pro třídu NumberSequence
''' </summary>
Public Class NumberSequenceEnumerator
    Implements IEnumerator

    Private _parent As NumberSequence
    Private _currentStep As Integer = -1

    ''' <summary>
    ''' Vytvoří novou instanci třídy
    ''' </summary>
    Public Sub New(ByVal _parent As NumberSequence)
        Me._parent = _parent
    End Sub

    Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
        Get
            'vrátit aktuální položku
            Return _parent.StartValue + _parent.Increment * _currentStep
        End Get
    End Property

    Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
        'přesunout se na další
        _currentStep += 1
        Return (Me.Current <= _parent.EndValue)      'pokud ještě nejsme na konci, vracet True
    End Function

    Public Sub Reset() Implements System.Collections.IEnumerator.Reset
        'nastavit enumerátor na začátek
        _currentStep = -1
    End Sub

End Class

V tomto enumerátoru si pamatujeme krok, na kterém právě stojíme, v proměnné _currentStep. Je nutné vědět, že před přístupem k prvnímu prvku se volá MoveNext, takže v počátečním stavu musíme být jakoby před prvním prvkem. Do tohoto počátečního stavu nás také musí dostat volání metody Reset.

Jinak je zajímavá akorát vlastnost Current, která vrací aktuální hodnotu, a funkce MoveNext, která přejde na další prvek. Jak se tyto hodnoty spočítají, to už je věc implementace našeho enumerátoru, tady je počítám. Samozřejmě když ale budete psát lineární spojový seznam, bude logika těchto metod trochu jiná, asi si budete pamatovat vrchol, na kterém jste, a v metodě MoveNext akorát přejdete na další, to už záleží na konkrétní situaci.

Poslední věc, která nám nyní zbývá, je říct třídě NumberSequence, že když na ni použijeme cyklus For Each, že se má procházet pomocí tohoto enumerátoru. Jak na to? Naimplementujeme rozhraní IEnumerable:

''' <summary>
''' Sekvence čísel
''' </summary>
Public Class NumberSequence
    Implements IEnumerable

    ...

    ''' <summary>
    ''' Vrátí novou instanci našeho enumerátoru
    ''' </summary>
    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return New NumberSequenceEnumerator(Me)
    End Function
End Class

A to je celé. Použití je pak velmi jednoduché:

        ' vypsat násobky 3 do 30
        For Each i As Integer In New NumberSequence(3, 30, 3)
            Console.WriteLine(i)
        Next
        Console.ReadKey()

Tímto vypíšete na obrazovku násobky tří v rozsahu od 3 do 30. Samozřejmě v praxi je tohle lepší dělat jednoduchým For cyklem, ale cílem tohoto článku bylo ukázat způsob, jakým si můžete napsat vlastní enumerátor, tohle je jen příklad.

Díky enumerátorům budete schopni napsat si vlastní kontejnery a datové struktury, které budou podporovat snadné procházení For cyklem. Většinu používaných datových struktur má .NET ve třídách System.Collections.Generic. Jinak samozřejmě od IEnumerable a IEnumerator existují i generické varianty.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback