6

Consider the following code, in a brand new WinForms .NET 4.0 application, with default settings:

Public Class Form1
  Private Sub AAA()
    Form1.AAA(Nothing) 'cannot refer to itself through its default instance; use 'Me' instead.
  End Sub

  Private Shared Sub AAA(str As String)
  End Sub
End Class

I am getting this error:

{FORM_CLASS_NAME} cannot refer to itself through its default instance; use 'Me' instead.

I also get this warning at the same line:

Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.

Assuming default instance is meant here, it ends up in an infinite loop - VS suggests to change Me.AAA() to Form1.AAA(), and then back. AAA() works in both.

Converting Private Sub AAA() to Shared solves the error. It seems like from Microsoft's point of view, all overloads must be shared, if at least one is. Or you get this default instance confusion. Why?

To clarify, I do not want to use default instance here, just do a shared call.

If anyone encountered the same situation, please advise.

Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151
  • I would recommend _not_ using default instances at all and declare the form variables when you need them. That's a stupid (IMHO) _compatibility_ feature to help migrate VB6 code. – Chris Dunaway Mar 12 '13 at 15:46
  • 1
    @ChrisDunaway: well, I am not using default instances here. But VS thinks I am. Calling Form1.AAA() from within Form1 is a perfectly valid shared method call. In C# this problem would have never existed. VB for some reason tends to sometimes think I am trying to use a default instance. – Victor Zakharov Mar 12 '13 at 15:48
  • Can you turn that off? I wonder if that's part of the "Application Framework" thing in the vb project settings. – Chris Dunaway Mar 12 '13 at 15:49
  • @ChrisDunaway: "Enable Application Framework" is disabled in my big app, I tried disabling it in my test app - no difference. That means default instance is not related to Application Framework. – Victor Zakharov Mar 12 '13 at 16:01
  • @ChrisDunaway: even though [it's suggested here](http://jmcilhinney.blogspot.ca/2009/07/vbnet-default-form-instances.html) that they are related. `The default instance is an object of that type that the VB application framework creates and manages for you.` – Victor Zakharov Mar 12 '13 at 16:08
  • In your 'bigger application' is this an error which is flagged in the IDE or does the project compile and then crash when it is run? – J... Mar 12 '13 at 16:51
  • @J...: It does not compile. – Victor Zakharov Mar 12 '13 at 17:22
  • @Neolisk can you give us some more code from the actual problem application? Are you making this call on the startup form? A different form? A form that inherits from something other than `Form`? Is the call in your larger application making the call from the `Load` handler? Have you overridden any of the `Form` members? What are your compile options and warning configuration? Obviously the code above works. We need to see the code that produces the problem to determine how it is causing the problem. – J... Mar 12 '13 at 18:07
  • @J...: I'll try my best to answer your questions here. In a big app, this form consists of 400 lines of code, not including the interface (InitializeComponent) and it depends on another 2 projects in the solution, both are our internal libraries, one is data access layer and another is UI building blocks. This form inherits from a `Form`, same as above. `Load` handler is not involved. No overrides. I am looking into your other questions. Thanks for some hints, btw. – Victor Zakharov Mar 12 '13 at 18:42
  • @J...: You got me pretty close to solution with your questions. After I stripped all irrelevant code from the form (I created a copy to play with it), the above construct started working. Now it's about figuring when it started working, i.e. removal of which code piece caused it. Must be somewhere in between those 400 lines. :) – Victor Zakharov Mar 12 '13 at 18:57

4 Answers4

4

Creating a variable alias that has the same name as the type of the Form class is without a doubt the single most disastrous VB.NET problem. But it was necessary to give VB6 developers a fighting chance to move to VB.NET.

The workaround is to stop trying to be explicit about what method you want to call. This compiles fine and is unambiguous, at least in your snippet:

  Private Sub AAA()
       AAA(Nothing)       '' fine
  End Sub

If that really, really hurts then simply swapping the two methods removes the ambiguity:

Private Shared Sub AAA(str As String)
End Sub

Private Sub AAA()
    Form1.AAA(Nothing)    '' fine
End Sub
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Swapping the methods, ha! That's amazing, @Hans - I would have never thought it could be the issue, always assuming method order is irrelevant in any .NET application. +1 and accepting. – Victor Zakharov Mar 12 '13 at 21:19
  • Be sure to document that well – djv Mar 13 '13 at 01:45
2

Can you get away with this? Your usage will be very similar Form1.AAA() vs. code.AAA().

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        code.AAA()
    End Sub

    Private Class code
        Public Shared Sub AAA()
        End Sub
    End Class

End Class
djv
  • 15,168
  • 7
  • 48
  • 72
  • +1. This definitely looks interesting as a workaround. The only concern is that it's somewhat non-intuitive, as it looks like calling shared members of another class. – Victor Zakharov Mar 12 '13 at 15:26
  • As a counter to my comment, you could rename `code` to `Form1Shared`. You introduce a maintainability concern, however, you make it more or less intuitive. – Victor Zakharov Mar 12 '13 at 15:32
1

EDIT

Given the new information in the OP - another solution to your issue may be to use optional parameters -- ie :

Private Shared Sub AAA(Optional ByVal str As String = Nothing)

Also - the resolution works out in the "right" way if you simply change the ordering of the declarations -- this avoids the compiler error:

Private Shared Sub AAA(ByVal str As String)
End Sub

Private Sub AAA()
    Form1.AAA(Nothing)
End Sub

--

Keeping this below because it can be helpful in other circumstances

Perhaps your larger application did something like this - VB is full of messes like this you can get yourself into. This will compile but it will crash :

Public Class Form1

    Private Shared Sub AAA()
        Form1.Text = "this"
    End Sub

    Private Sub Label1_TextChanged(sender As System.Object,  _
                                             e As System.EventArgs) _
                                             Handles Label1.TextChanged
        Form1.AAA()
    End Sub

End Class

Just the same, this actually is "fine" (I use the term loosely)...

Public Class Form1
    Private Shared dont As Boolean = True

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
                                                      Handles MyBase.Load
        dont = False
    End Sub

    Private Shared Sub AAA()
        If Not dont Then Form1.Text = "this"
    End Sub

    Private Sub Label1_TextChanged(sender As System.Object, _
                                               e As System.EventArgs) _
                                               Handles Label1.TextChanged
        Form1.AAA()
    End Sub

End Class

This is because the text changed handler will fire before Form1 completes loading (ie : during InitializeComponent()!) and will refer to the default instance which is not yet finished being created - so VB tries to create a new one for you so that you can call the shared method which spins you down the infinite loop.

Oddly, the Load handler is "fine" (again, loosely) to call Form1.AAA() in - as in your opening code - because the default instance (Form1 the instance of Form1 the Class) is finished creation at that point and another won't be created to satisfy the call. Any other code path, however, that starts in the shared call and ultimately ends up touching any instance data, no matter how torturous the path, will loop around and crash.

See also : Why is there a default instance of every form in VB.Net but not in C#?

Community
  • 1
  • 1
J...
  • 30,968
  • 6
  • 66
  • 143
  • My `Shared` sub does not call any of the default instance methods. The error happens at compile time. – Victor Zakharov Mar 12 '13 at 17:24
  • I updated the question, please have a look. Hope it makes more sense now. – Victor Zakharov Mar 12 '13 at 19:17
  • After having to fix several issues related to usage of optional parameters, I am now trying to avoid this pattern at all, if possible. – Victor Zakharov Mar 12 '13 at 20:13
  • @Neolisk - I wondered if you had duplicated names somewhere. Added a possible alternative. Hah... crossed in the post. Fair enough. I would also probably avoid implicit overloading between shared and instance members! – J... Mar 12 '13 at 20:13
  • Try the code I posted. Default namespace, default everything. No more custom code in the project. – Victor Zakharov Mar 12 '13 at 20:14
  • @Neolisk yes, I understand the issue now. Also added another suggestion. – J... Mar 12 '13 at 20:18
  • Unfortunately, I cannot accept more than 1 answer. But you definitely deserve a +1 for your efforts. So here you go, and big thanks for your help! – Victor Zakharov Mar 12 '13 at 21:21
  • @Neolisk Go figure... I suggested swapping declarations 20 minutes before Hans even answered. Glad it's sorted out, in any case! – J... Mar 13 '13 at 11:41
0

Unclear what you are trying to accomplish overall. In the OP Form1.AAA should be just AAA.

Private Sub AAA()
    AAA(Nothing)
End Sub
Private Sub AAA(str As String)
    If str IsNot Nothing Then MsgBox(str) ' else ???
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    AAA()
    AAA("hello")
End Sub
rheitzman
  • 2,247
  • 3
  • 20
  • 36
  • Disambiguing code is my goal. `AAA(Nothing)` can mean either a non-shared call or a shared call. `Form1.AAA(Nothing)` can only mean a shared call in the above case. Well it turned out that it could also mean a call to a default instance. But this was something unexpected. I generally prefer to have calls clearly state what they are and as such prefix shared calls with a class name, even if in the same class. – Victor Zakharov Mar 12 '13 at 21:47