I have a problem in a VB.Net class library which I have simplified greatly down to the following…
Public MustInherit Class TargetBase
End Class
Public Class TargetOne
Inherits TargetBase
End Class
Public Class TargetTwo
Inherits TargetBase
End Class
Public Class TargetManager
Public Sub UpdateTargets(ByVal Targets As List(Of TargetBase))
For Each objTarget As TargetBase In Targets
UpdateTarget(objTarget)
Next
End Sub
Private Sub UpdateTarget(ByVal Value As TargetOne)
End Sub
Private Sub UpdateTarget(ByVal Value As TargetTwo)
End Sub
End Class
This will not compile due to a syntax error on the UpdateTarget(objTarget) line – Overload resolution failed because no accessible ‘UpdateTarget’ can be called without a narrowing conversion
So I changed the For-Each loop to use Object instead of TargetBase…
For Each objTarget As Object In Targets
UpdateTarget(objTarget)
Next
This now compiles but I get a run-time error – Public member ‘UpdateTarget’ on type ‘TargetManager’ not found.
So I took the obvious next step of making the 2 UpdateTarget() overloads Public (instead of Private).
Public Sub UpdateTarget(ByVal Value As TargetOne)
End Sub
Public Sub UpdateTarget(ByVal Value As TargetTwo)
End Sub
This now works!
I can just about understand why changing it to Object would work but why must these methods be made Public when I am only calling them from within the same class – I would rather they weren’t available outside this class.
Can anybody explain this?
Thanks in advance (and sorry for the length of this question!)
Additional
Thanks for everyones answers so far. Ive got the workaround (make UpdateTarget methods Public) that makes it work. Another workaround would be to do a TypeOf check on objTarget and then DirectCast before calling UpdateTarget, like…
For Each objTarget As Object In Targets
If TypeOf objTarget Is TargetOne Then
UpdateTarget(DirectCast(objTarget, TargetOne))
ElseIf TypeOf objTarget Is TargetTwo Then
UpdateTarget(DirectCast(objTarget, TargetTwo))
End If
Next
This will also work – I posted the question because I really wanted to understand why changing visibility of UpdateTarget from Private to Public got rid of the run-time error, totally against my understanding!
Looks to me like it can’t decide what method to use because you are using the base type of both method parameters. TargetOne/Two are both valid TargetBases so both methods look the same to the resolution engine – meaning it can’t pick.However, I’ve no idea why the other changes make it work… let me think, update pending.
In C# I don’t get this issue because you cannot forward cast TargetBase to TargetOne or TargetTwo… it gives a different compiler error – argument for method is invalid because it cannot implicitly convert base to derived. The first compiler error you mentioned is basically the VB.NET equivalent.
I spotted this link, but I’m not sure if its for VB or VB.NET – either way, interesting read:
http://msdn.microsoft.com/en-us/library/tb18a48w.aspx
This also might relate to
Option Strict, and co-variance in VB.NET 2010. This article has a little overloads section, might prove useful:http://msdn.microsoft.com/en-us/magazine/ee336029.aspx
Update: note that I’ve no clue why its suddenly working, this sounds like one for Jon Skeet or Eric Lippert.
Update 2: one thing I can suggest is for each situation (private to public / use of object) compile the application and review the IL using Reflector. Basically, look for any differences – it could be that the compiler is adding something for you under the hood – either that, or the runtime is able to determine the correct method based on the current type.
Update 3: think I got it. This quote from the following link:
http://visualbasic.about.com/od/usingvbnet/a/earlybind.htm
Says that when you specify TargetBase, it’s early bound and compiler complains. When you specify object, it’s late bound and the runtime complains when its private re this link:
http://msdn.microsoft.com/en-us/library/h3xt2was(VS.80).aspx
Therefore specifying public works for you. The runtime is obviously able to late bind to the correct overload – a nice feature of late binding that was hidden by VB.NET for you 🙂