为什么结构中的引用类型的行为类似于值类型?

duo*_*gja 6 c# string value-type reference-type

我是C#编程的初学者.我现在正在研究strings,structs,value typesreference types.作为公认的答案,在这里和在这里,strings是具有存储在堆栈指针,而存储在堆的实际内容的引用类型.此外,如此处所述,structs是值类型.现在,我尝试练习structsstrings用一个小例子:

struct Person
{
    public string name;
}

class Program
{
    static void Main(string[] args)
    {
        Person person_1 = new Person();
        person_1.name = "Person 1";

        Person person_2 = person_1;
        person_2.name = "Person 2";

        Console.WriteLine(person_1.name);
        Console.WriteLine(person_2.name);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码片段输出

Person 1
Person 2
Run Code Online (Sandbox Code Playgroud)

这让我感到困惑.如果strings是引用类型并且structs是值类型,那么person_1.name和person_2.name应该指向堆上的相同空间区域,不应该是它们吗?

Eri*_*ert 13

字符串是引用类型,它们将指针存储在堆栈上,而它们的实际内容存储在堆上

不不不.首先,停止考虑堆栈和堆.在C#中,这几乎总是错误的思考方式.C#管理您的存储寿命.

其次,虽然引用可以实现为指针,但引用不是逻辑指针.参考文献是参考文献.C#有引用和指针.不要混淆它们.在C#中没有指向字符串的指针.有对字符串的引用.

第三,对字符串的引用可以存储在堆栈中,但也可以存储在堆上.如果有一个对string的引用数组,则数组内容在堆上.

现在让我们来看看你的实际问题.

    Person person_1 = new Person();
    person_1.name = "Person 1";
    Person person_2 = person_1; // This is the interesting line
    person_2.name = "Person 2";
Run Code Online (Sandbox Code Playgroud)

让我们用逻辑说明代码的作用.您的Person结构只不过是一个字符串引用,因此您的程序与以下内容相同:

string person_1_name = null; // That's what new does on a struct
person_1_name = "Person 1";
string person_2_name = person_1_name; // Now they refer to the same string
person_2_name = "Person 2"; // And now they refer to different strings
Run Code Online (Sandbox Code Playgroud)

当你说person2 = person1并不意味着变量person1现在是变量person2的别名.(在C#中有一种方法可以做到这一点,但这不是它.)这意味着"将person1的内容复制到person2".对字符串的引用是复制的值.

如果不清楚,请尝试绘制变量和箭头框以供参考; 复制结构时,会生成箭头的副本,而不是的副本.


InB*_*een 7

理解这一点的最好方法是完全理解变量是什么; 简单地说,变量是持有值的占位符.

那究竟是什么价值呢?在引用类型中,存储在变量中的值是给定对象的引用(可以说是地址).在值类型中,值是对象本身.

当你AnyType y = x;真正发生的事情是,存储的值的副本x被创建,然后存储y.

所以,如果x是引用类型,都xy将指向同一个对象,因为他们都将持有相同的参考的相同副本.如果x是一个值类型则两个xy将持有两个相同但不同的对象.

一旦你理解了这一点,就应该开始理解你的代码行为方式.让我们一步一步地研究它:

Person person_1 = new Person();
Run Code Online (Sandbox Code Playgroud)

好的,我们正在创建一个值类型的新实例.根据我之前解释的,值存储person_1是新创建的对象本身.存储此值的位置(堆或堆栈)是实现细节,它与代码的行为方式完全无关.

person_1.name = "Person 1";
Run Code Online (Sandbox Code Playgroud)

现在我们设置的变量name恰好是一个字段person_1.再次根据先前的解释,值name是指向存储器的存储器中的某处的引用string "Person 1".同样,存储值或字符串的地方是无关紧要的.

Person person_2 = person_1;
Run Code Online (Sandbox Code Playgroud)

好的,这是有趣的部分.这里发生了什么?好吧,存储的值的副本person_1被制作并存储在其中person_2.因为该值恰好是值类型的实例,所以创建并存储所述实例的新副本person_2.这个新副本有自己的字段name,存储在该变量中的值也是存储在(引用)中的值的副本.person_1.name"Person 1"

person_2.name = "Person 2";
Run Code Online (Sandbox Code Playgroud)

现在我们只是重新分配变量person_2.name.这意味着我们正在存储一个新的引用,指向string内存中的某个新内容.请注意,person_2.name最初持有存储的值的副本,person_1.name所以无论你做什么person_2.name都不会影响存储的任何值,person_1.name因为你只是在改变......是的,一个副本.这就是为什么你的代码的行为方式.

作为练习,尝试以类似的方式推断出代码如果Person是引用类型将如何表现.


usr*_*usr 6

每个struct实例都有自己的字段.person_1.name是一个独立变量person_2.name.这些不是 static字段.

person_2 = person_1 按值复制结构.

string不可变的事实不需要解释这种行为.

这是相同的情况,class而不是来证明区别:

class C { public string S; }

C c1 = new C();
C c2 = c1; //copy reference, share object
c1.S = "x"; //it appears that c2.S has been set simultaneously because it's the same object
Run Code Online (Sandbox Code Playgroud)

在这里,c1.Sc2.S参考相同的变量.如果你这样做,struct那么它们就变成了不同的变量(如代码中所示).c2 = c1然后输入struct值的副本,它之前是对象引用的副本.