Search code examples
vb.netoverloadingoverload-resolutionconstructor-overloading

Overload resolution works for normal method but not for constructor


My goal is to have a series of overloads, where the correct version of a method gets called depending on the type of the parameter (known only at runtime). However, I've run into an interesting problem in a case where the method I want to overload is a constructor.

Take the following inheritance structure:

Public MustInherit Class A
    Public Property Common As String
End Class

Public Class X
    Inherits A

    Public Property Unique1 As String
    Public Property Unique2 As String
End Class

Public Class Y
    Inherits A

    Public Property Unique3 As String
    Public Property Unique4 As String
End Class

Base class A is inherited by both X and Y.

Now take this class which I'll use to show the problem:

Public Class Foo
    Public Sub New(v As X)
        Common = v.Common
        Prop1 = v.Unique1
        Prop2 = v.Unique2
        Prop3 = "Some value"
        Prop3 = String.Empty
    End Sub

    Public Sub New(v As Y)
        Common = v.Common
        Prop1 = "Some value"
        Prop2 = String.Empty
        Prop3 = v.Unique3
        Prop4 = v.Unique4
    End Sub

    Public ReadOnly Property Common As String
    Public ReadOnly Property Prop1 As String
    Public ReadOnly Property Prop2 As String
    Public ReadOnly Property Prop3 As String
    Public ReadOnly Property Prop4 As String

    Public Shared Sub Bar(v As X)
    End Sub

    Public Shared Sub Bar(v As Y)
    End Sub
End Class

There is a normal method Bar with an overload, and also a constructor New with an overload. The first New has the same signature as the first Bar, and the second New has the same signature of the second Bar.

Finally take this test code:

Public Sub Test()
    Dim Param As Object = New X

    'This works fine
    Foo.Bar(Param)

    'This gives a compile error
    Dim Thing As New Foo(Param)
End Sub

The compiler seems to have no problem with the call to Bar, but for the constructor call I get the following compile error:

Overload resolution failed because no accessible 'New' can be called without a narrowing conversion:
'Public Sub New(v As X)': Argument matching parameter 'v' narrows from 'Object' to 'X'.
'Public Sub New(v As Y)': Argument matching parameter 'v' narrows from 'Object' to 'Y'.

Why does the constructor call cause an error while the call to Bar does not.

Also, if I change the Param declaration to Dim Param As A = New X, then neither of them will compile.

I feel like I should understand this one, but for whatever reason I don't. Could someone fill me in on why this doesn't work, and maybe suggest a work-around?


Solution

  • I'm sure I'll get plenty of people telling me again that I shouldn't be doing things this way, but here's what I did to actually solve the problem as I needed it:

    Public Class Foo
        Public Sub New(v As X)
        End Sub
    
        Public Sub New(v As Y)
        End Sub
    
        Public Shared Function Create(v As X) As Foo
            Return New Foo(v)
        End Function
    
        Public Shared Function Bar(v As Y) As Foo
            Return New Foo(v)
        End Function
    End Class
    

    Which lets me use Foo like this:

    Dim Param As Object = New Y
    Foo.Create(Param)
    

    Yes, the above uses late binding and loosely-typed code. But it also keeps redundant code to a minimum, requires no lengthy interface definition or implementation, is still completely predictable, and does exactly what I want. I do consider features like this useful and valid when used in the right context.

    I do still wish I could get some answer as to why the overload resolution works with a normal method but not a constructor. But for now I guess I'll just have to settle for this work-around.