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?
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.