【发布时间】:2018-03-04 13:55:11
【问题描述】:
在下面的代码中,我初始化了客户端并将其传递给新 Worker 类的构造函数。然后,我在两个工人仍在运行时将值设置为 null。据我了解,我正在对构造函数中的引用类型进行值传递,因此 p.myClient 和 worker1.client 应该指向堆上的同一个 Client 对象。但是为什么它不抛出 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();
}
}
【问题讨论】:
-
myClient是一个引用变量。它指向内存中的一个对象。当您将它(按值)传递给 Worker 时,worker 现在拥有对同一对象的自己的引用变量。稍后,当您将myClient设置为 null 时,您不会同时将其他引用重新指向 null。它们仍然指向原始对象。同样,如果你这样做:var foo = new Client(); var bar = foo; foo = null;,foo现在将是null,而bar仍然指向 foo 创建的对象。 -
引用类型的值是引用。
-
这可能会造成混淆,因为您可以使用引用修改对象,也可以修改它指向的地址,这是两件不同的事情。当您将
myClient传递给工作人员时,您是在说:“嘿,看看这个地址!”。如果稍后您修改了在该地址 的内容,例如更改属性值,那么在这种情况下,所有引用现在都将指向具有新值的同一个对象。不同之处在于,当您将 引用 分配给其他东西(例如null)时,您将为其提供一个新地址以指向它。您没有将一些新对象放在同一个地址。 -
谢谢大家现在明白了!
标签: c# .net multithreading pass-by-value