(ASP NET) ASP (2012)

Yield - і ці люди забороняли нам багато років ковирятися у носі?

Увесь пафос гарного коду багато-багато років будувався на лінейності коду, на відсутністі 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 - це образчик лінейності та передначертаності порядку виконання коду.

В реальних прогах с YIELD справи ще гірше. Наприклад, дуже часто YIELD використовується при роботі Reactive Extensions to .Net, ось лінкі на документацію на нього:

Це немаленька бібліотека, яка компілірується у спеціфічну StateMachine, яка виконує операції MoveNext у багатьох потоках одночасно. На цьому коді нище працює чотире потока .NET, у кожному з которих виконується YIELD.



Ця прога просто обходить файлову структуру, як будь-яка прога у моему блозі, наприкриклад ось ця - WinDump - снимок состояния системы с помощью WMI, але вона робіть це мультипоточно. І заповнює результатом обхода файлової сістеми Win-контрол TreeView.



Сподіваюсь, смисл цього коду зрозумілий. Дві змінні, які задані у стрічках 21 та 26, мають евенти, які додають до них бібліотека System.Reactive.DLL. На ці євенти підписуються у стрічках 73 та 84 хандлер outputDirectories або outputDirectory. Коли з'являэться перший підписчик, тоді сам RX викликає хандлер GetAllDirectories, заданий у стрічках 137 і далі, який працює у мультіпоточному режимі.


   1:  Imports System.Reactive.Concurrency
   2:  Imports System.Reactive.Linq
   3:  Imports System.Threading
   4:  Imports System.Collections.Generic
   5:  Imports System.ComponentModel
   6:  Imports System.Data
   7:  Imports System.Drawing
   8:  Imports System.Linq
   9:  Imports System.Text
  10:  Imports System.Windows.Forms
  11:  Imports System.IO
  12:   
  13:  ''' <summary>
  14:  ''' Provides an example of using the RxFramework to get directory listings using a background thread.
  15:  ''' </summary>
  16:  Partial Public Class DirectoriesForm
  17:   
  18:      ''' <summary>
  19:      ''' An observable providing a directory as soon as it is encountered.
  20:      ''' </summary>
  21:      Private directories As IObservable(Of String)
  22:   
  23:      ''' <summary>
  24:      ''' An observable providing directories in groups.
  25:      ''' </summary>
  26:      Private bufferedDirectories As IObservable(Of IList(Of String))
  27:   
  28:      ''' <summary>
  29:      ''' The current observer that is observing on one of the observables.
  30:      ''' </summary>
  31:      Private observer As IDisposable
  32:   
  33:      Public Sub New()
  34:          InitializeComponent()
  35:   
  36:          Dim syncContext = SynchronizationContext.Current
  37:   
  38:          ' Observe the enumeration of all directories using the winforms thread.
  39:          Me.directories = GetAllDirectories("c:\").
  40:              ToObservable(Scheduler.ThreadPool).
  41:              ObserveOn(syncContext)
  42:   
  43:          ' Observe the enumeratino of all directories, but buffered in groups 
  44:          ' of 1000 entries each second, and then observe that group on the winforms thread.
  45:          Me.bufferedDirectories = Observable.
  46:              Interval(TimeSpan.FromSeconds(1), Scheduler.ThreadPool).
  47:              Zip(
  48:              GetAllDirectories("c:\").
  49:              ToObservable(Scheduler.ThreadPool).
  50:              Buffer(1000),
  51:              Function(a, b) b
  52:              ).
  53:              ObserveOn(syncContext)
  54:   
  55:          Me.butStop.Enabled = False
  56:      End Sub
  57:   
  58:      Private Sub butStop_Click(sender As Object, e As EventArgs) Handles butStop.Click
  59:          ' Clear out the running observer
  60:          If Me.observer IsNot Nothing Then
  61:              Me.observer.Dispose()
  62:              Me.observer = Nothing
  63:              Me.butStop.Enabled = False
  64:              Me.butObserverSingle.Enabled = True
  65:              Me.butObserveBuffered.Enabled = True
  66:          End If
  67:      End Sub
  68:   
  69:      Private Sub butObserverSingle_Click(sender As Object, e As EventArgs) Handles butObserverSingle.Click
  70:          Me.treeViewDirectories.Nodes.Clear()
  71:          If Me.observer Is Nothing Then
  72:              ' Observe on the single directories.
  73:              Me.observer = Me.directories.Subscribe(AddressOf outputDirectory)
  74:              Me.butStop.Enabled = True
  75:              Me.butObserverSingle.Enabled = False
  76:              Me.butObserveBuffered.Enabled = False
  77:          End If
  78:      End Sub
  79:   
  80:      Private Sub butObserveBuffered_Click(sender As Object, e As EventArgs) Handles butObserveBuffered.Click
  81:          Me.treeViewDirectories.Nodes.Clear()
  82:          If Me.observer Is Nothing Then
  83:              ' observe on the buffered directories.
  84:              Me.observer = Me.bufferedDirectories.Subscribe(AddressOf outputDirectories)
  85:              Me.butStop.Enabled = True
  86:              Me.butObserverSingle.Enabled = False
  87:              Me.butObserveBuffered.Enabled = False
  88:          End If
  89:      End Sub
  90:   
  91:      Private Sub outputDirectory(path As String)
  92:          ' We check to see if the handle is created because when 
  93:          ' the form is disposing this may still be trying to observe.
  94:          If Me.treeViewDirectories.IsHandleCreated Then
  95:              Me.treeViewDirectories.Nodes.Add(path)
  96:          End If
  97:      End Sub
  98:   
  99:      Private treeNodes As New Dictionary(Of String, TreeNode)()
 100:   
 101:      Private Sub outputDirectories(paths As IEnumerable(Of String))
 102:          ' We check to see if the handle is created because when 
 103:          ' the form is disposing this may still be trying to observe.
 104:          If Me.treeViewDirectories.IsHandleCreated Then
 105:              Try
 106:                  Me.treeViewDirectories.BeginUpdate()
 107:                  For Each path__1 In paths
 108:                      Dim sb = New StringBuilder()
 109:                      Dim pieces = path__1.Split(Path.DirectorySeparatorChar)
 110:                      Dim parent As TreeNode = Nothing
 111:                      For i As Integer = 0 To pieces.Length - 1
 112:                          Dim child As TreeNode = Nothing
 113:                          sb.Append(pieces(i))
 114:                          sb.Append(Path.DirectorySeparatorChar)
 115:   
 116:                          If Not treeNodes.TryGetValue(sb.ToString(), child) Then
 117:                              If parent IsNot Nothing Then
 118:                                  child = parent.Nodes.Add(pieces(i))
 119:                              Else
 120:                                  child = Me.treeViewDirectories.Nodes.Add(pieces(i))
 121:                              End If
 122:                              treeNodes(sb.ToString()) = child
 123:                          End If
 124:                          parent = child
 125:                      Next
 126:                  Next
 127:              Finally
 128:                  Me.treeViewDirectories.EndUpdate()
 129:              End Try
 130:          End If
 131:      End Sub
 132:      ''' <summary>
 133:      ''' Gets the enumeratino of all directories and sub directories under the given path.
 134:      ''' </summary>
 135:      ''' <param name="path">The path to search</param>
 136:      ''' <returns>The enumeration of all directories and sub directories.</returns>
 137:      Private Shared Iterator Function GetAllDirectories(path As String) As IEnumerable(Of String)
 138:          Dim subdirs As String() = Nothing
 139:          ' Some directories may be inaccessable.
 140:          Try
 141:              subdirs = Directory.GetDirectories(path)
 142:          Catch generatedExceptionName As IOException
 143:          Catch generatedExceptionName As UnauthorizedAccessException
 144:          End Try
 145:          If subdirs IsNot Nothing Then
 146:              For Each subdir In subdirs
 147:                  Debug.Print(subdir.ToString)
 148:                  Yield subdir
 149:                  For Each grandchild In GetAllDirectories(subdir)
 150:                      Debug.Print(grandchild.ToString)
 151:                      Yield grandchild
 152:                  Next
 153:              Next
 154:          End If
 155:      End Function
 156:   
 157:  End Class




Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17
Link to this page: http://www.vb-net.com/Yield/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <MAIL ME>  <ABOUT ME>  < THANKS ME>