【问题标题】:What does assigning a static field to an instance field not point both at the same object将静态字段分配给实例字段不会同时指向同一个对象的原因是什么
【发布时间】:2015-06-28 03:02:02
【问题描述】:

In this fiddle,我们将静态字段分配给实例字段。当我们更改静态时,实例一仍然具有初始值。这是为什么?我们认为两者都应该指的是同一个对象,但似乎它们不是。

using System;

public class Program
{
    public static string StaticString;
    public string InstanceString;

    public static void Main()
    {
        Program.StaticString = "foo";

        var p = new Program();
        p.InstanceString = Program.StaticString;

        Program.StaticString = "bar";

        Console.WriteLine(p.InstanceString);
    }
}

我们真的希望它打印bar,但它打印了foo

【问题讨论】:

标签: c#


【解决方案1】:

在这个小提琴中,我们将一个静态字段分配给一个实例字段。

没错。这会将静态字段的当前值复制到实例字段。它不会永远将变量绑定在一起 - 它只是复制当前值。

当我们改变静态的时候,实例一仍然具有初始值。

是的,因为您刚刚更改了静态字段的值 - 为什么 更改实例字段的值?它们是自变量——它们只是碰巧在某一时刻保持相同的值。重要的是,您没有对实例字段的值所引用的字符串 object 进行任何更改。 (而且确实不能对字符串对象进行更改,因为字符串是不可变的。)

我将变量视为写有值的纸片。当变量是引用类型时,纸上的值只是导航到对象的一种方式——比如街道地址。但是将一个变量复制到另一个变量总是只是将写在一张纸上的内容复制到另一张纸上的问题。例如,假设我有:

House x = new House();
House y = x;
x.DoorColor = Black;
x = null;
Console.WriteLine(y.DoorColor);

就像这样:

  • 创建一张名为 x 的纸,并在其上写下街道地址
  • 将写在纸x 上的内容复制到一张名为y 的纸上
  • 去地址写在纸上x的房子,把门涂黑
  • 擦掉写在纸上的地址x
  • 去地址写在纸上y的房子,报告门的颜色

最后一步会报告门是黑色的,对吗?擦掉写在纸上的值 x 不会改变写在纸上的值 y 或任何关于房子的东西。

这与您的代码中的步骤并不完全相同,但希望它能提供更多信息...

【讨论】:

  • 我们不是按值传递引用吗?不应该有一个对象都指向一个对象吗?
  • @ShaunLuttin:是的,我们是。我们将值(这是一个引用)从静态字段复制到实例字段。但仅此而已 - 它只是复制该值。然后,您为静态字段提供不同的值 - 这不会更改实例字段中的值。请参阅我的编辑以获取更长的示例。
  • "...为什么会改变实例字段的值?"我(错误地?)认为字符串是对象,并且两个变量都指向同一个对象,所以如果我们改变一个的值,我们就会改变两个点的对象。
  • @ShaunLuttin:字符串 对象 - 最初两个变量 do 引用同一个对象。但这只是意味着,如果您以某种方式“通过”一个变量对对象进行更改,它将在另一个变量中看到。但这不是您在这里所做的 - 您只是更改静态字段的值以引用一些完全不同的对象。您需要非常清楚“更改变量的值”和“在变量的值所引用的对象内进行更改”之间的区别。
  • @ShaunLuttin:没错。您可能想尝试将代码更改为使用StringBuilder,而不是使用Program.StaticString = "bar",而是使用Program.StaticString.Append("bar")...
【解决方案2】:

当您在下面的语句中将StaticString 分配给实例字符串时

p.InstanceString = Program.StaticString;

运行时实际上会将InstanceString 指向字符串“foo”而不是StaticString,因此当您分配StaticString 值bar 时,运行时将创建一个字符串“bar”并将静态字符串指向它。注意实例字符串仍然指向我们拥有 foo 的内存位置。

我认为借助图表会更容易理解。这显示了 StaticString 和 InstanceString 实际上是如何指向值的。

【讨论】:

  • 为什么两个点都没有一个字符串对象?我似乎在这里遗漏了一些非常基本的东西。
  • StaticString 和 InstanceString 都指向 foo 的位置
【解决方案3】:

在设置为foo 之前的两行打印p.InstanceString。所以它应该像它一样打印foo

【讨论】:

    【解决方案4】:

    我把改变一个变量的 value 和改变一个参考点的 object 混淆了。

    字符串不可变的事实当然会使事情复杂化。 b = "foo"b = new string("foo".ToCharArray()) 完全一样。我们正在创建一个新对象并将其引用分配给b。我们不会改变现有对象。

    Here is an example that clarifies my mistake.

    using System;
    using System.Text;
    
    public class Program
    {
        public static StringBuilder StaticString;
        public StringBuilder InstanceString;
    
        public static void Main()
        {
            Program.StaticString = new StringBuilder("foo");
    
            var p = new Program();
            p.InstanceString = Program.StaticString;
    
            Console.WriteLine("Mutating the object at which a value points:");
            Program.StaticString.Append("bar");
            Console.WriteLine(p.InstanceString);
            Console.WriteLine(Program.StaticString);
    
            Console.WriteLine("\nChanging the value of a variable:");
            Program.StaticString = new StringBuilder("raboof");
            Console.WriteLine(p.InstanceString);
            Console.WriteLine(Program.StaticString);
        }
    }
    

    输出

    Mutating the object at which a value points:
    foobar
    foobar
    
    Changing the value of a variable:
    foobar
    raboof
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-19
      • 1970-01-01
      • 2021-04-17
      • 2014-02-22
      • 1970-01-01
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多