Warm tip: This article is reproduced from stackoverflow.com, please click
.net vb.net

Make Iterator function to return a null reference when an exception is thrown

发布于 2020-04-04 10:16:05

The following code iterates through a set of processors and returns their ids. Hardware exploration is hazardous and I want to make this function is as stable as possible. That is:

  • If an exception was thrown whilst preparing the iteration, return a null reference; and
  • If an exception was thrown during an iteration, simply skip this iteration, silently.

    Public ReadOnly Iterator Property ProcessorIds As IEnumerable(Of String) Implements IHardwareIds.ProcessorIds
        Get
    
            Try
    
                Dim BiosQuerry As ObjectQuery = New SelectQuery("Win32_Processor") 'https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processor
                Dim HardwarePieces As New ManagementObjectSearcher(BiosQuerry)
    
                For Each MyInfo As ManagementObject In HardwarePieces.Get
                    Try
                        Dim ProcessorId As String = MyInfo("ProcessorId").ToString
                        Yield ProcessorId
                    Catch ex As Exception
                    End Try
                Next
    
            Catch ex As Exception
                Return Nothing
            End Try
    
        End Get
    End Property
    

The current code does not compile because using return in an iterator is forbidden.

The best I could think of was to replace Return Nothing by Yield Nothing. I expect it should result in an enumerable containing one item, which is a null reference. Is this my only option? I know I could just use a regular function and return the collection once fully iterated. This is an exercise to practice with Iterators.

Questioner
Ama
Viewed
80
djv 2020-01-31 06:32

I don't see a way to cause an Iterator to return an empty collection upon exception, if that is what you really want to do. If you do, you could stop using Iterator and Yield, and simply do

Public ReadOnly Property ProcessorIds As IEnumerable(Of String) Implements IHardwareIds.ProcessorIds
    Get
        Try
            Using s As New ManagementObjectSearcher(New SelectQuery("Win32_Processor"))
                Return s.Get().OfType(Of ManagementObject).Select(Function(o) o("ProcessorId").ToString())
            End Using
        Catch
            Return Enumerable.Empty(Of String)
        End Try
    End Get
End Property

Using LINQ without the Iterator actually allows you to remove the For Each and simplify the code.

(If you would rather return null, it's almost the same)

Public ReadOnly Property ProcessorIds As IEnumerable(Of String) Implements IHardwareIds.ProcessorIds
    Get
        Try
            Using s As New ManagementObjectSearcher(New SelectQuery("Win32_Processor"))
                Return s.Get().OfType(Of ManagementObject).Select(Function(o) o("ProcessorId").ToString())
            End Using
        Catch
            Return Nothing
        End Try
    End Get
End Property

however, returning null has a different feel that other Enumerable functions (such as Where, which returns an empty collection when the predicate is not satisfied by any element, as pointed out by @Anu6is).

You can also stop iteration upon the first exception. If Yield is never hit, your result is an empty collection. I think this is more in the spirit of Iterator.

Public ReadOnly Iterator Property ProcessorIds As IEnumerable(Of String) Implements IHardwareIds.ProcessorIds
    Get
        Try
            Using s As New ManagementObjectSearcher(New SelectQuery("Win32_Processor"))
                For Each mo In s.Get()
                    Yield mo("ProcessorId").ToString()
                Next
            End Using
        Catch
            Return ' or Exit Property
        End Try
    End Get
End Property

The C# analog is yield break; and it seems quite clean. See also this answer.