In the below code I initialized the client and passed it to the constructor of the new Worker classes. I then set the value to null while the two workers are still running. To my understanding I'm doing a pass by value for reference type in the constructor, so p.myClient and worker1.client should point to the same Client object on the heap. But why doesn't it throw a NullPointerException?
class Client
{
public Client()
{
Console.WriteLine("creating client");
}
public void Go(string s)
{
Console.WriteLine("go {0}", s);
}
}
class Worker
{
private int invokeCount;
Client client;
public Worker(Client c)
{
client = c;
}
public void Pull(Object stateInfo)
{
Console.WriteLine("{0} Pulling {1,2}.",
DateTime.Now.ToString("h:mm:ss.fff"), (++invokeCount).ToString());
client.Go("pull");
}
public void Push(Object stateInfo)
{
Console.WriteLine("{0} Pushing {1,2}.",
DateTime.Now.ToString("h:mm:ss.fff"), (++invokeCount).ToString());
client.Go("push");
}
}
class Program
{
Client myClient = new Client();
static void Main()
{
Program p = new Program();
var worker1 = new Worker(p.myClient);
Console.WriteLine("{0:h:mm:ss.fff} Creating timer.\n", DateTime.Now);
var stateTimer = new Timer(worker1.Push, null, 1000, 400);
var worker2 = new Worker(p.myClient);
Console.WriteLine("{0:h:mm:ss.fff} Creating timer.\n", DateTime.Now);
var stateTimer2 = new Timer(worker2.Pull, null, 1000, 400);
p.myClient = null;
Console.ReadKey();
stateTimer.Dispose();
Console.WriteLine("\nDestroying timer.");
Console.Read();
}
}
You trying to discuss too many things at once: "pass by value", "reference type" and "heap".
In C#, the only way to not pass by value is to use out
or ref
. They require a variable (local, field, property, parameter), whereas pass by value requires only an expression (a value).
But, none of those concepts are even relevant.
The key is that assignment is a shallow, value-copying operation.
client = c;
makes a copy of the value in the field. No other code sets that field so it keeps the same value "forever". (You possbily should use the readonly
modifier.)
p.myClient = null
removes p
's copy of the value. With no other copy of that value available (worker1.client
and workder2.client
are private), it can no longer use it. (A good static analyzer would flag that statement there because the variable being set is not read at any place afterward, raising the question of your code being an incomplete thought.)