【问题标题】:string acts like a value type字符串就像一个值类型
【发布时间】:2016-11-17 16:31:52
【问题描述】:

string 是引用类型,但作用类似于值类型。

string a,b;
a=b="test"; //As i understood a and b have to point to the same location in the heap

a="a";
Console.WriteLine("a: {0}, b: {1}",a,b);

结果它打印出 a: a, b: test 请解释为什么对a 所做的更改没有反映在b 上,正如根据引用类型规则可以预期的那样。

【问题讨论】:

  • 我认为你的理解是错误的。 ab 是它们自己的变量,因此是它们自己的值。您正在考虑的是参考类型。这些是值类型。此外,字符串是不可变的,这意味着当它们改变时,它们会改变整个事物。
  • 您的问题与值类型与引用类型无关。 ab 是不同的变量,因此分配给一个变量不会影响另一个变量。
  • @RichardBarker 你错了,引用类型和值类型都会出现这种行为。字符串也不是值类型。最后,您所说的关于 Immutable 的内容有点模棱两可,Immutable 的意思是您所做的任何更改都会返回一个带有更改的新对象,但原始对象未修改,如果要使用新对象,则必须用新对象覆盖变量对象。
  • 感谢指正。
  • 除了string 是不可变的事实之外,a="a"; 正在为变量分配一个新的对象引用,而不是试图改变变量指向的对象。

标签: c# string types reference


【解决方案1】:

因为您没有更改 a 指向的值 - 您更改了引用本身。例如,这与 C 中的相同 - 不要想象 a = "Hi" 正在改变 *a

事实上,在不安全的代码之外,没有办法改变现有的字符串。 .NET 中的字符串是不可变的。您只需要创建新字符串或更改引用,这意味着 string 确实具有值语义,就像任何其他不可变数据类型一样。这是一个设计选择 - 字符串很难,这使得它们更简单。

赋值运算符在 C# 中不可重写 - 它始终意味着“为标识符赋值”。对于string,值是字符串的reference,而不是字符串本身。但是更改一个本地的引用不会更改其他本地的引用。会怎样?它不是指向指针的指针。

让我们更好地说明引用类型和值类型之间的区别:

void Main()
{
  A a = default(A), b = default(A);

  a = b = new A { Number = 42 };
  a.Number = 10;

  Console.WriteLine(a); // 42
  Console.WriteLine(b); // 10
}

public struct A
{
  public int Number;
}

现在,A 是一个值类型,所以当我们将b 分配给a 时,我们实际上做了一个副本。两个本地人不指向同一个对象。因此,当a.Number 更改为 10 时,b.Number 保持 42。

但是,如果您将声明更改为 public class A,则赋值只会更改引用的值 - 换句话说,仅存在一个 A 实例。然后,a.Number = 10 实际上也改变了b.Number - 两者在内存中的位置相同。

如果将其与 C 进行比较,就好像本地值类型是值本身,而本地引用类型是指向该值的 指针。所以,是这样的:

// Value-type
A a, b;

b.Number = 42;
a = b;
a.Number = 10; // Only the value in a is changed

// Reference-type
A *a, *b;

a = b = new A(42);
a->Number = 10; // Both locals point to the changed value

这与 C++ 引用的工作方式完全不同 - 在 C# 中,这些引用仅限于方法参数。因此,例如,C# 中的交换方法如下所示:

void Main()
{
  int a = 42, b = 10;

  Swap(ref a, ref b);

  Console.WriteLine(a); // 10
  Console.WriteLine(b); // 42
}

public void Swap(ref int a, ref int b)
{
  int x = 0;

  SwapI(ref a, ref b, ref x);
}

// No ref locals, so we need a helper method
private void SwapI(ref int a, ref int b, ref int x)
{
  x = a;
  a = b;
  b = x;
}

由于我们要切换的参数是ref,所以无论类型是值类型还是引用类型,它们总是具有引用语义。

【讨论】:

    【解决方案2】:

    您没有更改a 指向的对象,您只是更改了变量a。这对于字符串也没有什么特别之处,您可以对任何类型执行此操作并获得完全相同的行为。

    class Foo
    {
        private int _i;
    
        public Foo(int i)
        {
            _i = i;
        }
    
        public override string ToString()
        {
            return _i.ToString();
        }
    }
    
    Foo a, b;
    a=b= new Foo(1);
    
    a=new Foo(2);
    
    Console.WriteLine("a: {0}, b: {1}",a,b);
    

    请参阅我对this question 的回答,我将深入类比变量与其指向的对象之间的差异。

    【讨论】:

      【解决方案3】:

      //你初始化了变量——太好了!
      字符串 a,b;

      //a="test"b="test"
      a=b="测试";

      //现在 a="a"b 仍然是 "test"
      a="a";


      要让 b 显示为“a”,您必须将其设置为“a”。设置变量的唯一方法是在变量位于左侧的位置进行声明。因此,要使 b 等于 "a",您必须执行以下操作:
      b=a; 在最后一行之后。所以你的 3 行看起来像这样:

      a=b="测试";
      a="a";
      //新行
      b=a;
      现在当你输入 Console.WriteLine("a: {0}, b: {1}",a,b);
      你得到 a: a, b: a

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-02
        • 1970-01-01
        • 2011-03-10
        • 2014-06-29
        • 1970-01-01
        • 2012-02-14
        • 2021-12-31
        • 2019-07-06
        相关资源
        最近更新 更多