vblang
vblang copied to clipboard
[Proposal] Ease the pain of using non-generic (object) collections in For Each
I have this code:
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm As XmlSchemaElement In HtmlSchema.Elements.Values
Select GetItem(elm.Name)
End Function
And I get the error:
Option Strict On disallows implicit conversions from 'Object' to 'XmlSchemaElement'.
I have two solutions:
1- Use Option Strict Off which I don't want to do for all the file.
2- Use explicit cast
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm In HtmlSchema.Elements.Values
Select GetItem(CType(elm, XmlSchemaElement).Name)
End Function
which is a long of unnecessary code.
I need two things here:
- A way to make VB accept the implicit casting in for each. If VB will not do that by default , maybe we can use
[]around the type as an indication.
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm As [XmlSchemaElement] In HtmlSchema.Elements.Values
Select GetItem(elm.Name)
End Function
this syntax is accepted today as the [] are used for keyword dis-ambiguity. If this is not acceptable for some consideration, we can instead use it around As type:
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm [As XmlSchemaElement] In HtmlSchema.Elements.Values
Select GetItem(elm.Name)
End Function
- I want VB to infer the non-generic collection's element type from its indexer. I know that may break existing code, so what I am asking if to auto add
[As XmlSchemaElement]in the for each / from , or offer it as the suggested type in auto complete.
How bad would LINQ's .Cast(Of XmlSchemaElement)() or .OfType(Of XmlSchemaElement)() methods be in your situation?
@gilfusion
Same unnecessary code. The big issue that I waste time to view the collection definition to find the exact item type. As I explained, I want to ease my mind of all of this, and focus on programming. This is a backward compatibility hell which must find a solution. I hope .NET Core rewrite all non-generic collection to a generic version in NuGets, so we can reference them in new apps, without breaking old code.
@VBAndCs
So, ultimately, your hope is that all those collections that enumerate on Object fade away. I can't argue with that.
Still, is this
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm [As XmlSchemaElement] In HtmlSchema.Elements.Values
Select GetItem(elm.Name)
End Function
much more of an improvement than this?
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm In HtmlSchema.Elements.Values.Cast(Of XmlSchemaElement)
Select GetItem(elm.Name)
End Function
From your description, it sounds like they would do roughly the same thing, just replacing a method call in one place with syntax in a different place.
Does LINQ to XML work for this?
@AdamSpeight2008 The issue here is to do things in a suitable lang syntax, Clearly there are many ways to do the cast, which is not my concern here. As I mentioned, I waste time to review the collection detention just to get the its exact element type, to use it as the type of the iteration var if option Strict is Off, otherwise I have to do an explicit cast. My two points are:
- VB can infer the element type from the collection indexer.
- VB can tryst the implicit cast from object to this type in
For EachandFrom, so it can ignoreOption Explicit Onin this spot, or define a syntax to turn this option off at some definitions (say using[]as[As type]), which can be a general solution for similar situations.
@VBAndCs, I'm still confused. What's the difference between specifying the type of the indexer and inferring the cast, which you're suggesting, and specifying the cast and inferring the type of the indexer, which is possible today? Either way, it looks like you need to give the type somewhere.
I have to agree with @VBAndCs here. If, as in the example provided, it is possible to cast the enumerated result into the resulting type within the loop... specifying that As Whatever type should be enough to instruct the compiler to "just do it". We shouldn't require any language change here to have the system to accomplish this. With that said, I will make a comment about Option Strict Off... "don't want to do for all the file." This isn't as bad as you make it out to be if you combine with Partial Classes. When I encounter this, I simply create a new file NameOfOriginalFile.Late.vb as a Partial Class to the original and extract the code that needs to be late into this other file. It's not as bad as it sounds since it also makes it clear that I have Late Bound code floating around in my project that I might want to review when time allows for possible alternate solutions. In any case, back to the original proposal... this doesn't sound like a language change... it sounds like something is "broken" and should be fixed. To me this is something that just "should work". It does if you turn the option off, it doesn't if you turn it on. So it clearly can do the deed. It also knows the target type that is desired... so just do it already. ;-)
Good idea, but:
- I still need to find the collection element type myself, which is a time waste.
- This will turn the project to a miss if I have to create say 20 partial classes, each having only one method just to solve this issue!
So, I am a fan of making language designers work some more to save the developers from this agony.
For me this proposal feels a lot like hacking the VB language when the .NET Framework should be fixed insted. The problem here is, that some collection should have implemented the generic ICollection(Of T) instead of ICollection. What I tend to do in this case is to write an extension method as workaround:
Namespace Global.System.Xml.Schema
<HideModuleName>
Public Module XmlSchemaExtensions
<Extension>
Public Function GetValues(table As XmlSchemaObjectTable) As IEnumerable(Of XmlSchemaElement)
Return table.Values.Cast(Of XmlSchemaElement)
End Function
End Module
End Namespace
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From elm In HtmlSchema.Elements.GetValues()
Select GetItem(elm.Name)
End Function
In this particular case, however, XmlSchemaElement may not always be right. There could also be an XmlSchemaAttribute, see https://docs.microsoft.com/de-de/dotnet/api/system.xml.schema.xmlschemaobjecttable. You might want to use .OfType(Of XmlSchemaElement) or use .Cast(Of XmlSchemaObject) instead. Yet, it seems, you only need the Name property anyway:
Protected Function GetAlwaysVisibleItems() As IEnumerable(Of CompletionItem)
Return From name In HtmlSchema.Elements.Names.Cast(Of String)()
Select GetItem(name)
End Function
A few other common examples, where the IEnumerable implementation is messed up:
''' <remarks>Workaround to missing <see cref="ICollection(Of String)" /> implementation of <see cref="StringCollection" />.</remarks>
<Extension>
Public Function AsEnumerable(collection As StringCollection) As IEnumerable(Of String)
Return collection.Cast(Of String)
End Function
''' <remarks>Workaround to missing <see cref="ICollection(Of Match)" /> implementation of <see cref="MatchCollection" />.</remarks>
<Extension>
Public Function AsEnumerable(collection As MatchCollection) As IEnumerable(Of Match)
Return collection.Cast(Of Match)
End Function
''' <remarks>Workaround to missing <see cref="IEnumerable(Of Byte)" /> implementation of <see cref="BitArray" />.</remarks>
<Extension>
Public Function AsEnumerable(array As BitArray) As IEnumerable(Of Boolean)
Return array.Cast(Of Boolean)
End Function
<Extension>
Public Function AsEnumerable(collection As UIElementCollection) As IEnumerable(Of UIElement)
Return collection.Cast(Of UIElement)
End Function