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 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 Iterator
s.
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.
Glad you declared that MOS with a
Using
statement. As a note, the query result objects should be inspected in aforeach
loop (as shown in the question), thenDim ProcessorId As String = MyInfo("ProcessorId")?.ToString()
andyield ProcessorId
if not null. In the end, you'll get an empty collection if no results are found. The empty catch block (in the question) is ugly and not needed. If you catch an exception (you shouldn't; if you do get it, something went wrong at this point), you have to evaluate whether to return null, since you have a partial result caused by an exception.