3

Is there any way to have immutable parameters in a C# method? Below I have some code for a messenger application that is giving me some issues. The idea is that there is one server that can handle multiple clients. The code works by creating a TcpListener that watches for incoming connections, getting the IP address of the underlying TcpClient (which is sent through the NetworkStream as a string), starting a new thread which will handle messages from that client, and then listening for more connections. The main problem is that after the client object is passed into the new Thread, the main code loops around and sets the value of client to null while waiting for another connection.

Error type: System.InvalidOperationException Error message: The operation is not allowed on non-connected sockets.

What this says to me is that the TcpClient's value inside the handleMessages thread is being affected by what happens to client after the thread is initiated by the .Start() method.

Here's my code:

private void watchForConnections()
{
    TcpListener listener = new TcpListener(IPAddress.Any, this.Port); //Listener that listens on the specified port for incoming clients
    listener.Start();
    TcpClient client = new TcpClient();
    do
    {
        client = listener.AcceptTcpClient(); //Wait for connection request
        StreamReader reader = new StreamReader(client.GetStream());
        string clientIP = reader.ReadLine(); //Use the StreamReader to read the IP address of the client
        RelayMessage("SERVER_NEW_" + clientIP + "_" + DateTime.Now.ToString("HH:mm:ss")); //Tell all machines who just connected
        Thread messageWatcher = new Thread(() => handleMessages(client));
        messageWatcher.Start(); //Start new thread to listen for messages from that specific client
    } while (AllowingConnections == true);
    listener.Stop();
}

private void handleMessages(TcpClient _client)
{
    using (StreamReader readMsg = new StreamReader(_client.GetStream())) //I get the error here
    using (StreamWriter writeMsg = new StreamWriter(_client.GetStream())) //And here
    {
        //Handle messages from client here
    }
}

My question: is there any way to have a parameter in handleMessages that will not be affected by what happens OUTSIDE of the method? My research so far has turned up nothing about this, or anything like it. What I need is kind of like the opposite of a ref parameter. Maybe I'm just not searching the right things. I don't know if I'm even explaining it right.

Another example with strings:

string data = "hello";
Thread doStuff = new Thread(() => DoStuff(data)); //Value of data is equal to: "hello"
doStuff.Start();
data = "goodbye"; //I want the value of the string in the doStuff thread to still be: "hello", not "goodbye" (Don't reflect changes made to string after thread is started/method is called)

If anything's not clear, please let me know! I'm probably just as confused as you!

UPDATE/SOLUTION: For anyone who needs it in the future, this is how the problem was resolved, as per Viru's answer:

Thread messageWatcher = new Thread(() => handleMessages(client.Clone());
Micah Vertal
  • 564
  • 1
  • 7
  • 16
  • Why don't you call it like `DoStuff(string.Copy(data))`, thus creating a new unrelated copy of `data`? – dotNET Feb 28 '16 at 05:03
  • create copy. `var copy = data`? – M.kazem Akhgary Feb 28 '16 at 05:07
  • That has been discussed here: http://stackoverflow.com/questions/2339074/can-parameters-be-constant – Steve Wellens Feb 28 '16 at 05:07
  • @dotNET string is immutable, you need not copy the data before passing it another method – Viru Feb 28 '16 at 05:21
  • One of my favorite things to do is simply when you create a new project you just change a minor detail of C# to F# and then all of a sudden all your variables are immutable. – Luke Xu Feb 28 '16 at 05:25
  • @Viru: I know that. The problem is not related to mutability. The problem is that the new thread might not have started by the time `data = "goodbye";` line is executed, thus sending in the wrong value to the thread. He must create a copy before sending it in. – dotNET Feb 28 '16 at 05:28
  • @dotNET oh yes...that is closure problem...Anyway, I think OP actually had problem with TCPClient instance.... – Viru Feb 28 '16 at 05:30
  • @dotNET but I still think solution you mentioned is incorrect. string.Copy will be also deffered excuted so when you change data to goodbye....it will actually pass goodbye to method....Correct apporach would be to copy it local instance and pass it to method – Viru Feb 28 '16 at 05:36

1 Answers1

2

How about using Clone method. This will create recreate the object in new memory. So your calling method instance will not impact instance in HandleMessage method

 Thread messageWatcher = new Thread(() => handleMessages(client.Clone()));

TCPClient is of reference type.. So irrespective of you pass it as ref or not the memory where the object is stored will be shared..When you pass an object as ref then stack as well as memory is shared between methods. When you pass the object between methods without ref parameter, you do not allocate new memory for your object rather create a new stack entry which points to same memory location. In this case, you need to copy data to new memory location so Clone will do exactly that.

About your second example. string is immutable so when you pass the value in DoStuff method through thread and change the value in calling method then it will not affect the DoStuff method. Every time you initialized\reassign a string a new memory is allocated. I do not think behavior you are stating for string example is possible.Unless as pointed by @Dotnet, thread might have not started meanwhile you have reassigned the value to goodbye...This is called closure and way to solve it is to create a copy of that string variable and pass that copy in the method

 var copy = data; 
Thread doStuff = new Thread(() => DoStuff(copy));
Viru
  • 2,228
  • 2
  • 17
  • 28
  • Thanks for the quick response; this is just what I was looking for! Do you happen to have any ideas as to what the title of the question should be? – Micah Vertal Feb 28 '16 at 05:27
  • Okay, I just wasn't sure if the title was clearly related to the problem. Thanks again! – Micah Vertal Feb 28 '16 at 14:00