【问题标题】:c#: after pass by value for a reference type, would changing the value outside affect the object?c#:通过引用类型的值传递后,更改外部值会影响对象吗?
【发布时间】: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


【解决方案1】:

您试图一次讨论太多事情:“按值传递”、“引用类型”和“堆”。

在 C# 中,不按值传递的唯一方法是使用 outref。它们需要一个变量(本地、字段、property、 参数),而按值传递只需要一个表达式(一个值)。

但是,这些概念都无关紧要。

关键是赋值是一种浅层的值复制操作。

client = c; 复制字段中的值。没有其他代码设置该字段,因此它“永远”保持相同的值。 (您可能应该使用readonly 修饰符。)

p.myClient = null 删除 p 的值副本。由于没有该值的其他副本可用(worker1.clientworkder2.client 是私有的),它不能再使用它。 (一个好的静态分析器会在此处标记该语句,因为之后在任何地方都不会读取正在设置的变量,从而提出您的代码是一个不完整的想法的问题。)

【讨论】:

  • 属性不归类为变量,除了 ref 返回属性(以及任何其他 ref 返回方法)。
  • @PetSerAl 你是对的:CS0206 A property or indexer may not be passed as an out or ref parameter。谢谢。提供引用属性或索引器将对并发特性产生影响。
猜你喜欢
  • 1970-01-01
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
  • 2017-10-21
  • 2018-04-03
  • 2014-02-08
相关资源
最近更新 更多