string是一种引用类型(类似于对象)。为什么在以下场景中的diff行为 - 创建字符串和对象的实例时?
我理解字符串中不变性等的概念,但这与分配给字符串的值(不会改变)有关。我的问题更多的是为什么s2正在创建另一个完整的实例,而如果我已经完成了对象它不会那样做?
字符串
的示例class Program
{
static void Main(string[] args)
{
string s1 = "Hello";
string s2 = s1;
s1 = null;
Console.WriteLine("s1 = " + s1);
Console.WriteLine("s2 = " + s2);
Console.ReadLine();
}
}
输出:s2仍然打印“Hello”
对象示例
class Program
{
static void Main(string[] args)
{
Name s1 = new Name();
s1.id = 5;
Name s2 = s1;
s1 = null;
Console.WriteLine("s1 = " + s1.id);
Console.WriteLine("s2 = " + s2.id);
Console.ReadLine();
}
}
public class Name
{
public int id { get; set; }
}
输出:s1和s2均为空;
答案 0 :(得分:7)
在您的第二个代码段中,s2
不为空,但您无法看到它,因为s1.id
会引发NullReferenceException
并且您没有处理它。所以你的程序永远不会到达第二行。移动它,你会在5
中看到Console
。在s1 = null;
后,s2
没有' t change.It仍将s1's
旧位置指向内存。
此外,如果您不想删除第一行,您可以将其放在try/catch
块内:
try
{
Console.WriteLine("s1 = " + s1.id);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("s2 = " + s2.id);
您可以在5
和异常消息中看到Console
。但这完全没必要;它肯定会引发异常,因为s1
null 。因此: string
与除immutability
之外的其他引用类型没有区别
答案 1 :(得分:6)
首先,我确信你会发现你将获得很多NullReferenceException
代码。
其次,字符串类是一个有趣且特殊的object
,您可以在心理上将其视为primitive
数据类型。我只想说,System.String
不是原始类型 ....但您可以将其视为一般用途。
我已经绘制了一些图表,可以更好地显示您给出的每个示例中发生的事情。
当你
string s1 = "Hello";
string s2 = s1;
创建一个名为s1的String类型的对象,该对象指向一个包含值"Hello"
的内存空间。然后创建一个名为s2的字符串类型的新对象,并将来自s1的"Hello"
值复制到另一个内存空间并由s2
寻址。这会使你的记忆看起来像下面那样。
现在,当你
s1 = null;
您已将s1
的引用设置为指向null pointer
,这将使该值为“Hello”的内存浮动,直到垃圾收集器出现并将其删除。此时s1
仍然指向先前复制的值。
现在让我们看一下它与普通对象的区别。
当你
Name s1 = new Name();
s1.id = 5;
Name s2 = s1;
创建一个名为s1
的内存空间,它指向内存中新分配的空间,该空间足以容纳Name
对象的内存。
然后,您指向s1
中寻址的内存空间,并将其名为id
的属性更改为5。
然后你创建了一个名为s2
的新内存空间,它将地址的值复制到上面提到的分配内存中。所以现在s1
和s2
指向内存中的同一个对象。这会使你的记忆看起来像下面那样。
注意:此时,如果您要更改id
的值,则s1
和s2
会更改,如果是是您用于id
的可空类型,并且您将其设置为null
,此更改将反映在s1
和s2
中。
现在,如果你要
s1 = null;
正如你在帖子中所做的那样。 s1
会将其地址更改为指向null pointer
,s2
将继续查看内存中的相同空间。这导致您在下面看到的内容。
编辑:进一步解释为什么字符串看起来像基元而不是对象。
我不知道,不过我会推测。
我的猜测是它是以这种方式设计的,因为开发人员想要使用string
就好像它是一个原语。我会说,在我对其进行更改之前,不必担心对我的字符串的杂散引用,或者必须clone
它。但是,Microsoft无法使string
成为原始类型,因为字符串很大。 string
的最大长度是Int32
的最大值。并且每个字母都是char
,这是一个Int16
,所以我相信大约2场演出(不要引用我的数学)。这意味着字符串太大而无法存储在堆栈上,只有1MB。因此字符串必须是对象并放在堆上。
如果您不知道堆栈和堆之间的差异,我建议您查找它,知道如何处理内存是很好的,而在C#中,如果您决定将所有脏工作完成,转到类似C ++的东西,你会发现自己管理它(不是非常有趣)。
所以简而言之,我认为这是因为我们想要使用像基元这样的字符串,但由于堆栈的限制,它们被实现为对象。
答案 2 :(得分:1)
您的变量是引用变量,因为它们是使用String
类型声明的。您的代码正在执行此操作:
Hello
s1
s2
并将其引用分配给s1
s1
对null
在整个过程中,字符串Hello
仍在内存中,s2
仍然引用它。 s1
和s2
是两个不同的容器;他们每个人都可以自己参考。参考文献是否相同是无关紧要的。
我会尝试一个比喻:
你(s2
)和我(s1
)坐在一个房间里,看着桌子上的一个花瓶(Hello
)。这类似于s1
和s2
指向内存中的相同字符串,就像它们在Main
方法中的第二行之后所做的那样。现在,我起身离开房间(s1 = null;
)你还在看着花瓶,不是吗?我们是两个不同的人(变量),有能力看待不同的事物。