Yield/Iterator/IEnumerable - і ці люди забороняли нам багато років колупатися у носі?
Увесь пафос гарного коду багато-багато років будувався на лінійності коду, на відсутності GOTO та стрибків між фрагментами коду. Дозволеним вважалися лише IF та цикли. Ось подивиться, будь ласка на цю істерію щодо GOTO.
А тепер подивиться, будь ласка, як працює оператор нової версії бейсіку YIELD:
1: Module Module1
2:
3: Sub Main()
4:
5: ' Output: A1,3,A2,5,A3,8,A4
6: For Each number As Integer In SomeNumbers()
7: Debug.Print(number)
8: Next
9:
10: ' Output: B1,2,B2,B1,4,B2,B1,8,B2,B1,16,B2,B1,32,B2,B1,64,B2,B1,128,B2,B1,256,B2
11: For Each number In Power(2, 8)
12: Debug.Print(number)
13: Next
14:
15: ' Output: C1,Sun,C2,C1,Mon,C2,C1,Tue,C2,C1,Wed,C2,C1,Thu,C2,C1,Fri,C2,C1,Sat,C2
16: Dim days As New DaysOfTheWeek()
17: For Each day As String In days
18: Debug.Print(day)
19: Next
20:
21: ' Output: D1,1,D2,2,D3
22: Dim iterateSequence = Iterator Function() _
23: As IEnumerable(Of Integer)
24: Debug.Print("D1")
25: Yield 1
26: Debug.Print("D2")
27: Yield 2
28: Debug.Print("D3")
29: End Function
30:
31: For Each number As Integer In iterateSequence()
32: Debug.Print(number)
33: Next
34:
35:
36: ' Output: E1,E2,E7,E3,E4,5,E5,E4,6,E5,E4,7,E5,E4,8,E5,E4,9,E5,E4,10,E5,E6
37: For Each number As Integer In GetSequence(5, 10)
38: Debug.Print(number)
39: Next
40:
41: ' Output: F2,F1,F7,Tadpole 400,F3,F1,F7,Pinwheel 25,F4,F1,F7,Milky Way 0,F5,F1,F7,Andromeda 3,F6
42: Dim theGalaxies As New Galaxies
43: For Each theGalaxy In theGalaxies.NextGalaxy
44: Debug.Print("F7")
45: With theGalaxy
46: Debug.Print(.Name & " " & .MegaLightYears)
47: End With
48: Next
49: Console.ReadKey()
50:
51: End Sub
52:
53: Private Iterator Function SomeNumbers() As System.Collections.IEnumerable
54: Debug.Print("A1")
55: Yield 3
56: Debug.Print("A2")
57: Yield 5
58: Debug.Print("A3")
59: Yield 8
60: Debug.Print("A4")
61: End Function
62:
63: Private Iterator Function Power(ByVal base As Integer, ByVal highExponent As Integer) _
64: As System.Collections.Generic.IEnumerable(Of Integer)
65:
66: Dim result = 1
67: For counter = 1 To highExponent
68: result = result * base
69: Debug.Print("B1")
70: Yield result
71: Debug.Print("B2")
72: Next
73: End Function
74:
75: Private Function GetSequence(ByVal low As Integer, ByVal high As Integer) As IEnumerable
76: Debug.Print("E1")
77: If low < 1 Then
78: Throw New ArgumentException("E!")
79: End If
80: Debug.Print("E2")
81: '
82: Dim iterateSequence = Iterator Function() As IEnumerable
83: Debug.Print("E3")
84: For index = low To high
85: Debug.Print("E4")
86: Yield index
87: Debug.Print("E5")
88: Next
89: Debug.Print("E6")
90: End Function
91: Debug.Print("E7")
92: Return iterateSequence()
93: End Function
94:
95:
96: End Module
97:
98:
99: Public Class DaysOfTheWeek
100: Implements IEnumerable
101:
102: Public days = New String() {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}
103:
104: Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
105:
106: For i As Integer = 0 To days.Length - 1
107: Debug.Print("C1")
108: Yield days(i)
109: Debug.Print("C2")
110: Next
111: End Function
112: End Class
113:
114: Public Class Galaxies
115: Public ReadOnly Iterator Property NextGalaxy _
116: As System.Collections.Generic.IEnumerable(Of Galaxy)
117: Get
118: Debug.Print("F2")
119: Yield New Galaxy With {.Name = "Tadpole", .MegaLightYears = 400}
120: Debug.Print("F3")
121: Yield New Galaxy With {.Name = "Pinwheel", .MegaLightYears = 25}
122: Debug.Print("F4")
123: Yield New Galaxy With {.Name = "Milky Way", .MegaLightYears = 0}
124: Debug.Print("F5")
125: Yield New Galaxy With {.Name = "Andromeda", .MegaLightYears = 3}
126: Debug.Print("F6")
127: End Get
128: End Property
129: End Class
130:
131: Public Class Galaxy
132: Public Sub New()
133: Debug.Print("F1")
134: End Sub
135: Public Property Name As String
136: Public Property MegaLightYears As Integer
137: End Class
Сподіваюсь, друзі - ви зрозуміли, що трапиться в коді вище. Які б слова не казали, які в команди ми не писали, але виконуються зовсім різні клаптики коду в такому порядку, який ще потрібно зрозуміти. Будь-яка крихітна помилка приведе до зовсім іншому порядку виконання всіх клаптиків коду. Про яке тут GOTO взагалі мова може йти? Код, побудований на основі GOTO - порівняно з YIELD - це образчик лінійності та передбачуваності порядку виконання коду.
Other topic, related with IEnumerable interface.
- Serialize Table to CSV with Iterator and Yeld
- Generic VB.NET function for loading ExcelFile to DataBase (EF6 CodeFirst) with Reflection and avoiding Linq-to-Entity Linq.Expressions (use Linq-to-Object IEnumerable)
- Define IEnumerable extension interface to Hierarchy LinqToSQL and LinqToObject
|