Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this an error in the VB.NET compiler or by design?

I've found a difference in overload resolution between the C# and the VB-compiler. I'm not sure if it's an error or by design:

Public Class Class1
    Public Sub ThisBreaks()

        ' These work '
        Foo(Of String)(Function() String.Empty) 'Expression overload '
        Foo(String.Empty) 'T overload '

        ' This breaks '
        Foo(Function() String.Empty)
    End Sub

    Public Sub Foo(Of T)(ByVal value As T)

    End Sub

    Public Sub Foo(Of T)(ByVal expression As Expression(Of Func(Of T)))

    End Sub
End Class

Note that it doesn't matter if the overloaded Foo-methods are defined in VB or not. The only thing that matters is that the call site is in VB.

The VB-compiler will report an error:

Overload resolution failed because no accessible 'Foo' is most specific for these arguments:

'Public Sub Foo(Of String)(expression As System.Linq.Expressions.Expression(Of System.Func(Of String)))': Not most specific.

'Public Sub Foo(Of )(value As )': Not most specific.


Adding the C# code which works for comparison:

class Class1
{
    public void ThisDoesntBreakInCSharp()
    {
        Foo<string>(() => string.Empty);
        Foo(string.Empty);
        Foo(() => string.Empty);
    }

    public void Foo<T>(T value)
    {

    }

    public void Foo<T>(Expression<Func<T>> expression)
    {

    }
}
like image 235
Patrik Hägne Avatar asked Apr 11 '11 11:04

Patrik Hägne


2 Answers

Ignoring for the moment the assumption that "if the C# compiler does it, it must be right, therefore it's a bug in the VB compiler". I can immediately see the ambiguity:

Foo(Function() String.Empty)

Could invoke the T version, substituting Func(Of String) for T. Or it could reclassify the single line lambda into an expression tree, and invoke the Expression(Of Func(Of String)) method. There's no reason one should be preferred over the other, and in fact VB is stopping you from proceeding without forcing you to specify which one you want.

like image 148
Damien_The_Unbeliever Avatar answered Oct 16 '22 09:10

Damien_The_Unbeliever


I'm pretty sure I've found the reason for this and it is not a short coming of the VB-compiler but it is a short coming of the C# compiler.

Consider the following which is legal in VB:

Dim foo = Function() String.Empty

The equivalent would not be legal in c#:

var foo = () => string.Empty;

So, type inference is a bit stronger in VB, because of this the argument in the example Function() String.Empty can be inferred to Function(Of String) which would be applicable to the Foo(Of T)(ByVal value As T) overload.

In C# this can not happen since the () => string.Empty can never be inferred without context, it can be inferred in the expression overload but not in the T-overload.

like image 21
Patrik Hägne Avatar answered Oct 16 '22 10:10

Patrik Hägne