Visual Basic Development Bookmark and Share   
 Home > Visual Basic Language > VB.Net and C# allow implicit narrowing conversion in for each with Option Strict On
 

VB.Net and C# allow implicit narrowing conversion in for each with Option Strict On

Okay, am I going crazy, or is VB.Net just showing its true colors again?
When I turn Option Strict On, I am still able to do implicit narrowing conversions in a for each loop.

For example, this code actually compiles;

  Private Sub foobar()
    Dim foo() As foo = {New foo}
    For Each bar As bar In foo
    Next
  End Sub

  Private Class foo
  End Class

  Private Class bar
    Inherits foo
  End Class


From my understanding of Option Strict, this should not work. See here "Restricts implicit data type conversions to only widening conversions". My code above is a narrowing conversion, and so should be illegal. It will fail at runtime with an InvalidCastException.

C# does the same thing. Below is another implicit narrowing conversion.

        private void foobar()
        {
            foo[] foo={new foo()};
            foreach(bar bar in foo){
            }
        }

        private class foo { }
        private class bar: foo { }




Collin Sauve
Collin Sauve  15 hours 29 minutes ago
Yes, this a got'cha of For Each/foreach. It hales from the early days of .net - before generics. For instance, this code works:

Option Strict On
Option Explicit On

Module Module1

    Class Foo

    End Class

    Class Bar
        Inherits Foo
    End Class

    Sub Main()
        Dim f As New ArrayList()
        f.Add(New Foo())

        For Each g As Foo In f

        Next
    End Sub

End Module

Basically, it made things a bit easier on developers - at the expense of a bit of correctness. In fact, For Each/foreach don't even require that the type being enumerated implement IEnumerable or IEnumerable<Of T>. All it requires is that the object have a method named GetEnumerator that returns and IEnumerator object:

Option Strict On
Option Explicit On

Module Module1

    Class Foo
        Public Function GetEnumerator() As IEnumerator
            Return Nothing
        End Function


    End Class


    Sub Main()
        Dim f As New Foo

        For Each g As Foo In f

        Next
    End Sub

End Module

Obviously, the above doesn't work - but, it will compile :)

Tom Shelton
Tom Shelton  13 hours 20 minutes ago
Just noticed that in a for each I am also able to do an implicit conversion for which a converter has been defined;

    Dim s() As String = {"1","foo"}
    For Each i As Integer In s
    Next


At runtime, the first element will use convert.toint32 to make i=1, but will throw an invalidcastexception on the 2nd element.
This should fail at compile time if Option Strict is On.
Collin Sauve
Collin Sauve  15 hours 1 minutes ago
Bar is not a collection of Foo objects.
Mark the best replies as answers. "Fooling computers since 1971."
Rudedog2  14 hours 14 minutes ago
Bar is not a collection of Foo objects.
Mark the best replies as answers. "Fooling computers since 1971."

I think you are mis-reading the code and the question. Bar inherits Foo. I am enumerating an array of Foo and converting each element to Bar, which is an implicit narrowing conversion and therefore should be illegal.

Collin Sauve
Collin Sauve  13 hours 55 minutes ago
You're right.
I had changed and edited the content of the post.

Foo is not a collection of Bar objects.

That is what it should read.
An array of Foo objects, base class, is not a collection of Bar objects, derived class.


Public Sub foobar()
Dim foo() As foo = {New foo(), New foo()} ' this is an array of base class objects.
For Each bar As bar In foo 'invalid cast at runtime, cannot base to derived class
Next
End Sub



Widening and Narrowing Conversions

"A widening conversion changes a value to a data type that can accommodate any possible value of the original data.
A narrowing conversion changes a value to a data type that might not be able to hold some of the possible values."

A derived class can hold any and every field of any base type.
A base class cannot hold any and every field of any derived type.

That code should not compile.


Rudy =8^D

Mark the best replies as answers. "Fooling computers since 1971."
  • Edited byRudedog2 13 hours 26 minutes agobold text
  •  
Rudedog2  13 hours 27 minutes ago
Yes, this a got'cha of For Each/foreach. It hales from the early days of .net - before generics. For instance, this code works:

Option Strict On
Option Explicit On

Module Module1

    Class Foo

    End Class

    Class Bar
        Inherits Foo
    End Class

    Sub Main()
        Dim f As New ArrayList()
        f.Add(New Foo())

        For Each g As Foo In f

        Next
    End Sub

End Module

Basically, it made things a bit easier on developers - at the expense of a bit of correctness. In fact, For Each/foreach don't even require that the type being enumerated implement IEnumerable or IEnumerable<Of T>. All it requires is that the object have a method named GetEnumerator that returns and IEnumerator object:

Option Strict On
Option Explicit On

Module Module1

    Class Foo
        Public Function GetEnumerator() As IEnumerator
            Return Nothing
        End Function


    End Class


    Sub Main()
        Dim f As New Foo

        For Each g As Foo In f

        Next
    End Sub

End Module

Obviously, the above doesn't work - but, it will compile :)

Tom Shelton
Tom Shelton  13 hours 20 minutes ago
It compiles for historical reasons. In fact, I wish I could find it - but there was a video from the .NET 1.0 days that explained why the designers chose to implement this behavior (it was in the context of C#, but I'm sure the same logic applied). It was really a bow to the fact that .NET did not support generics at that time.

Tom Shelton
Tom Shelton  12 hours 56 minutes ago
Thanks Tom,
It makes sense that they would leave that in for backwards compatibility, I just wish they offered an option similar to Option Strict that would make this raise a compiler error. This is certainly a "Got'cha" indeed.
Collin Sauve
Collin Sauve  12 hours 19 minutes ago
I just realized one strong reason why the code should compile.

You could declare your own types that implement the IEnumerable interace and perform their own type conversion and casting.
Such a type would be a custom collection where you would define the behavior.

Rudy =8^D

Mark the best replies as answers. "Fooling computers since 1971."
Rudedog2  11 hours 14 minutes ago

You can use google to search for other answers

Custom Search

More Threads

• Change dataGridView DataSource
• Can you have a maskedtextbox data entry go from right to left?
• form settings using ApplicationSettingsBase
• Dynamic Threading
• error changing summary information
• Finding the Computer Id number
• How do I count rows in a dataset or loop through a column?
• Docking Forms
• About name of files
• Query with <asp:RequiredFieldValidator>