-1

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();
    }
}
psbox2021
  • 43
  • 5
  • 2
    `myClient` is a reference variable. It points to an object in memory. When you pass it (by value) to a Worker, then the worker now has it's own reference variable to the same object. Later, when you set `myClient` to null, you are not also re-pointing the other references to null. They still point to the original object. Similarly, if you do: `var foo = new Client(); var bar = foo; foo = null;`, `foo` will now be `null`, while `bar` still points to the object that foo created. – Rufus L Sep 22 '17 at 22:37
  • The value of a reference type **is** the reference. – Equalsk Sep 22 '17 at 22:43
  • It can be confusing because you can modify an object with a reference and you can modify the address it points to, which are two different things. When you pass `myClient` to a worker, you're saying, "hey, look at this address!". And if later you modify something *at that address*, like change a property value, then in that case, all the references will now be pointing to the same object with the new value. The difference is, when you assign *the reference* to something else (like `null`), then you're giving it a new address to point to. You're not putting some new object at the same address. – Rufus L Sep 22 '17 at 22:50
  • Thank you all make sense now! – psbox2021 Sep 23 '17 at 00:16

1 Answers1

1

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.)

Tom Blodget
  • 20,260
  • 3
  • 39
  • 72
  • 2
    Property is not classified as variable, except for ref return properties (as well as any other ref return methods). – user4003407 Sep 23 '17 at 04:34
  • @PetSerAl You are right: `CS0206 A property or indexer may not be passed as an out or ref parameter`. Thanks. Providing for referencing a property or indexer would have implications for concurrency characteristics. – Tom Blodget Sep 23 '17 at 13:49