Question scope of variables causing me difficulties (unnecessarily)

Dave Kimble

Member
Joined
Nov 20, 2012
Messages
8
Programming Experience
10+
Due to the constraints forced upon me by the structure of OOP programs
and the constraints of VB.NET's scoping of variables,
problems with variables' scope are getting in the way of me doing any work.

I can appreciate that if you have many people writing different parts of the same program,
then scoping the variable might avoid name clashes, but is there any way of switching scoping off when there is only one person writing the whole thing ?

Alternatively, is there a syntax that makes all variables accessible for everywhere?

Specifically, this doesn't work - the socket opened has port=0 :

VB.NET:
Public Class insserver
    Public Shared port As Integer
    Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
    
    Private Sub doConfig()
        port = 'something
    End Sub 
    
    Private Sub openSocket()
        server.Start()
    End Sub
End Class

and this doesn't even compile :

VB.NET:
Public Class insserver
    Public Shared port As Integer
        
    Private Sub doConfig()
        port = 'something
    End Sub 
    
    Private Sub openSocket()
        Public Shared server As New  System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
        server.Start()
    End Sub
End Class

and this doesn't allow other subs to access server :

VB.NET:
Public Class insserver
    Public Shared port As Integer
        
    Private Sub doConfig()
        port = 'something
    End Sub 
    
    Private Sub openSocket()
        Dim server As New  System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
        server.Start()
    End Sub
End Class
 
It's not scoping of variables that is causing you issues. It's the fact that you are writing bad code.

In the first example, you are creating the TcpListener before a value has been assigned to the 'port' variable. That's like building a car and then saying that you'll drive it to the petrol station to get its first tank of fuel. If it's got no fuel, how are you going to drive it anywhere? The 'port' variable isn't set until the 'doConfig' method is called so you can't create the TcpListener until then either. Don't use the 'port' variable for anything until you've set the value of the 'port' variable.

The solution to all this is simple. Set the value of the 'port' and 'server' variables in the same place. They must both be declared at the class level if they are to be used in more than one method so either set the value of both where they are declared or set the value of both in the 'doConfig' method. Don't set one in one place and one in the other.
 
It's not scoping of variables that is causing you issues. It's the fact that you are writing bad code.

Thanks for your reply.
I am aware that the 3 examples are bad code, because none of them work. But if I declared the variable "server" in the sub doConfig, following the assignment of "port", then that is just another case of example 3 - I can't then access "server" in openSocket or any of the other places I need it.

If I set them both at the Class level, like in example 1, then the assignment of "port" hasn't happened yet.
 
You are confusing declaring a variable and setting its value. In the first example you are declaring both variables at the class level, which is good. You are then setting the value of the 'server' variable where it's declared while you are setting the value of the 'port' variable in the 'doConfig' method, which is bad. As I already told in my previous post, either set both variables where they are declared or both in the 'doConfig' method. The issue has nothing whatsoever to do with scope and is purely the fact that you are using a variable before you have assigned it a value.
 
Maybe I haven't explained myself properly, or maybe you stopped reading after finding the thing wrong with Example 1. I know Examples 1 - 3 are ALL wrong, and I know why. I understand the difference between declaring a variable and assigning a value to it.

What you seem to be suggesting is:

VB.NET:
Public Class insserver         
Private Sub doConfig()
        Dim port As Integer
        port = 'something         
        Dim server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)     
End Sub           
    Private Sub openSocket()         
        server.Start()     
    End Sub 
End Class
but that will give an error against server.start() :
" 'server' is not declared. It may be inaccessible due to its protection level"
That seems to be a scoping problem to me.

I can't put the declarations at the Class level, because then I don't get to assign "port".
And I can't put the declarations in doConfig() because then "server" is not accessible from openSocket() , monitorSocket() and closeSocket().
And I can't put the declaration of "server" in openSocket() because then it is not accessible from monitorSocket() and closeSocket().
So what can I do ?

I know what I want to achieve, but scoping is preventing me from doing it, and has been for 3 days now. Excuse me if I'm getting a bit cranky, but why does anybody want scoping anyway ?
 
No, that is not what I was suggesting. Now you're declaring the variables inside the method. I specifically stated that the variables should be declared at the class level, i.e. OUTSIDE any methods, exactly as you were doing originally. The issue is where you're setting the values of those variables, not where you're declaring them. As I said, either set both values where the variables are declared, i.e. outside the method, or set both values inside the method. If you choose the second option that does NOT mean that the variables get declared inside the method.
 
I'm assuming this isn't the full code of this class, but it might also help if you review the Public, Private, and Shared modifiers to ensure that they are what you want. Step back and consider how this class will be used. Are the port and server variables generic to all instances of the class? If not they shouldn't be Shared. Do you want the port and server variables to be able to be changed by code outside of this class? If not, they should be ReadOnly. There are several other questions that I could ask.

In the different versions of this code that you provided, these variables are shifted around into various locations, which changes how and where they can be used. I think you need to take a timeout from writing the code and determine what the overall goal is that you're trying to achieve.

While Example 1 could easily be fixed by just initializing 'port' where it is declared, I think you may be heading down a path full of other problems. Perhaps not, but this looks a bit fishy to me, which is why I've posed the questions about the overall goal of the class.
 
Are the port and server variables generic to all instances of the class? If not they shouldn't be Shared.

There is only one instance of the class, and there are no other classes. "Public Shared" is probably overkill as far as extending the scope is concerned, but for the time being I just want something that will work.

The value of "Port" is derived by parsing an .INI file (not shown), so has to be done in the method doConfig() .

I am writing this in VB.NET as a rewrite of a PHP (CLI) script, which I wrote and tested in a few hours.

jmcilhinney: Obviously I must be missing something in what you are saying, but I don't know what it is. Instead of repeating yourself again, could you SHOW me by correcting this code?
VB.NET:
Public Class insserver
     Public Shared port As Integer
     Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
     Private Sub doConfig()
         port = 'result of parsing .INI file
    End Sub

    Private Sub openSocket()
         server.Start()
     End Sub 
End Class
 
Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
That is the short form for :
Public Shared server As TcpListener = New TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)

The first part is declaring the variable and its type, = is the assignment operator, the second part creates the object.
What jmcilhinney has been trying to explain to you is that you can declare the variable (the class field), but you have to wait until port is known before you can create the TcpListener object and assign it to the variable.
 
There is only one instance of the class, and there are no other classes. "Public Shared" is probably overkill as far as extending the scope is concerned, but for the time being I just want something that will work.

The value of "Port" is derived by parsing an .INI file (not shown), so has to be done in the method doConfig() .

I am writing this in VB.NET as a rewrite of a PHP (CLI) script, which I wrote and tested in a few hours.

jmcilhinney: Obviously I must be missing something in what you are saying, but I don't know what it is. Instead of repeating yourself again, could you SHOW me by correcting this code?
VB.NET:
Public Class insserver
     Public Shared port As Integer
     Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
     Private Sub doConfig()
         port = 'result of parsing .INI file
    End Sub

    Private Sub openSocket()
         server.Start()
     End Sub 
End Class
This is what's bugging me about my php class now, it has no variable types, no variable scope and it's a mess to keep track of everything. Luckily vb.net is a compiled language and therefor has a lot more rules to help keep everything straight as I code. What everyone is trying to tell you is in order to declare a new TcpListener you have to have 2 things: the server & the port, since you're wanting a class for this we can make those two properties of the class. Also let's make a couple of constructors for that class, one with no parameters and one with the server and the port. Then let's make a single sub that will allow us to instantiate a TcpListener & start the server:
Imports System.Net

Public Class InsServer

    Private m_Port As Integer
    Private m_IpAddy As IPAddress
    Private m_Server As Sockets.TcpListener = Nothing

    Private m_ServerIsRunning As Boolean

    Public Sub New()
        Me.Server = "127.0.0.1"
        Me.Port = 0
        m_ServerIsRunning = False
    End Sub

    Public Sub New(ByVal Port As Integer)
        Me.Server = "127.0.0.1"
        Me.Port = Port
        m_ServerIsRunning = False
    End Sub

    Public Sub New(ByVal Server As String, ByVal Port As Integer)
        Me.Server = Server
        m_Port = Port
        m_ServerIsRunning = False
    End Sub

    Public Property Server() As String
        Get
            Return m_IpAddy.ToString
        End Get
        Set(ByVal value As String)
            If IPAddress.TryParse(value, m_IpAddy) Then
                If m_ServerIsRunning Then Call Me.Start()
            Else
                Throw New ArgumentException(String.Format("IP Address of '{0}' is invalid", Server))
            End If
        End Set
    End Property

    Public Property Port() As Integer
        Get
            Return m_Port
        End Get
        Set(ByVal value As Integer)
            If value >= 0 Then
                m_Port = value
                If m_ServerIsRunning Then Call Me.Start()
            Else
                Throw New ArgumentException("Port must be greater than 0")
            End If
        End Set
    End Property

    Public Sub Start()
        If m_ServerIsRunning AndAlso m_Server IsNot Nothing Then
            'Stop it if it's already running
            m_Server.Stop()
        End If
        m_Server = New Sockets.TcpListener(m_IpAddy, m_Port)
        m_Server.Start()
        m_ServerIsRunning = True
    End Sub

End Class
Now an example of using it:
Public Class Form1

    Private m_iServer As New InsServer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        m_iServer.Port = 123 'Value from ini file
        m_iServer.Start()
    End Sub

End Class
Or even:
Public Class Form1

    Private m_iServer As New InsServer(123) 'value from ini file

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        m_iServer.Start()
    End Sub

End Class
None of your problems is with the scope of your variables; you're just not declaring, setting, and then using them correctly at all.
 
VB.NET:
Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddres  s.Parse("127.0.0.1"), port)
is the short form of
VB.NET:
Public Shared server As TcpListener = New TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)

Well I didn't know that, so maybe we are getting somewhere.
So if I have at the class level:
VB.NET:
Public Shared server As [FONT=courier new]System.Net.Sockets.[/FONT]TcpListener
and after port has been evaluated:
VB.NET:
server = New TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
that should do it, but it doesn't - I get a compiler error
"Error 7 'server' is already declared as 'Public Shared server As System.Net.Sockets.TcpListener' in this class. "

So "server" seems to be declared OK in the first line, but in the second line instead of setting a value for it, it seems to be treating it as another declaration. It does the same thing if I omit the "New". How can "server = " be treated as a declaration ?


JuggaloBrotha's lines 7 and 53 seems to me to be what I have done.
Anyway, I'm sure we are all heartily sick of this, so unless I'm a whisker away from getting it right, I'm going to give VB.NET away.
Thanks for all your help.
 
VB.NET:
Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddres  s.Parse("127.0.0.1"), port)
is the short form of
VB.NET:
Public Shared server As TcpListener = New TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)

Well I didn't know that, so maybe we are getting somewhere.
So if I have at the class level:
VB.NET:
Public Shared server As [FONT=courier new]System.Net.Sockets.[/FONT]TcpListener
and after port has been evaluated:
VB.NET:
server = New TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
that should do it, but it doesn't - I get a compiler error
"Error 7 'server' is already declared as 'Public Shared server As System.Net.Sockets.TcpListener' in this class. "

So "server" seems to be declared OK in the first line, but in the second line instead of setting a value for it, it seems to be treating it as another declaration. It does the same thing if I omit the "New". How can "server = " be treated as a declaration ?


JuggaloBrotha's lines 7 and 53 seems to me to be what I have done.
Anyway, I'm sure we are all heartily sick of this, so unless I'm a whisker away from getting it right, I'm going to give VB.NET away.
Thanks for all your help.
Take a look at my entire InsServer class, I'm sure you'll notice that I only have 1 spot where any of the 3 important variables are declared (all three right at the top of the class). Everything else simply instantiates and/or uses them, this is no different from how PHP works (other than declaring what type each variables is and keeping scope in mind).

Before you give up on vb.net completely here, have you even tried using that class I provided?
If my class does not work as intended, we would need to see your entire code because I've only made that class based on the very little code you've posted so far.
 
have you even tried using that class I provided?

Not exactly, but what I tried next was to use that part of it, that I was embedding in my code, in a separate solution on its own:
VB.NET:
Imports System.Net
 
Public Class InsServer
 [INDENT]Private port As Integer
    Private localhost As IPAddress
    Private server As Sockets.TcpListener
    
 Private Sub initialise(sender As Object, e As EventArgs) Handles MyBase.Load
        doConfig()
        openSocket()
 End Sub

    Public Sub doConfig()
     port = 2000
     localhost = Dns.GetHostEntry("localhost").AddressList(0)
End Sub
 
    Public Sub openSocket()
             server = New Sockets.TcpListener(localhost, port)        
     server.Start()
End Sub
[/INDENT]
  
End Class

and that complied and ran OK (port tested with TCPView).

So I went back to my original solution and found a piece of code in there that I certainly never wrote. It was something like:
VB.NET:
Private Function server(...    )
End Fun
and it was THAT that was causing the 'already declared' error, as well as wavy blue line under my "server = ...."

Finally can I just clarify what would have been the model answer to my first post:
Split
Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port) into
Public Shared server As System.Net.Sockets.TcpListener
and this after evaluating "port":
server = New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)

Solved, thanks.
 
Last edited:
Not exactly, but what I tried next was to use that part of it, that I was embedding in my code, in a separate solution on its own:
Seems like you're still trying to take bits and pieces of code from the various sources and mashing them all together without understanding what any of the pieces really do. If you use my entire class, I'm sure you will see that it works as you're trying to get your code to do (plus some).
Finally can I just clarify what would have been the model answer to my first post:
Split
Public Shared server As New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port) into
Public Shared server As System.Net.Sockets.TcpListener
and this after evaluating "port":
server = New System.Net.Sockets.TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), port)
The underlying portion of my class does exactly that, plus I even added in properties to easily allow you to change the ip address & the port with checks to make sure that they're valid values and all that proper stuff.

I am curious though, why are you using the "Shared" keyword? There's absolutely no reason to use the shared keyword here, do you even know what "shared" does in vb.net?
 
Seems like you're still trying to take bits and pieces of code from the various sources and mashing them all together without understanding what any of the pieces really do.

Yes, that's more or less what I'm doing. Isn't that allowed ? :wink:
I have the programs (server and client) already written in PHP, so I just have to learn the new rules.
Did you know you don't have to use classes, methods, properties etc. just to write a GUI app ? In fact it makes it MUCH harder and less readable and more error-prone. Enforced variable typing makes it harder again. On the other hand VB.NET proves you don't need $ and ; littered everywhere to write a language that a compiler can understand.
I would stick with PHP-WinBinder if only they hadn't stopped supporting it before getting it completely working and documented.

I am curious though, why are you using the "Shared" keyword?

Some tutorial said "Public shared" had the widest scope. My original problem was that if I declared "server" in openSocket(), I couldn't use it in monitorSocket() - a scoping problem. Any fool can see that the "server" in monitorSocket() is the same thing as the "server" in openSocket(), but the VB.NET compiler doesn't see it that way. It's a hindrance rather than a help.
I'm now using "Private" for my declarations and it seems to be working OK.
 
Back
Top