只是为了了解引用类型的内容以及基于Eric Lippert http://blogs.msdn.com/b/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx的文章,我想深入理解为什么这段代码会像这样工作
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
MyCalleeClass calleeClass = new MyCalleeClass();
calleeClass.MyTestProp = "toto";
MyCallerClass callerClass = new MyCallerClass();
callerClass.TestMethod(calleeClass);
//normally given that a class it's a reference type this should give a toto 1
Console.WriteLine(calleeClass.MyTestProp);
}
}
class MyCalleeClass
{
public string MyTestProp { get; set; }
}
class MyCallerClass
{
public void TestMethod(MyCalleeClass calleeClass)
{
Console.WriteLine("In the caller method");
//does this object is created on the stack or on the heap
calleeClass = new MyCalleeClass();
calleeClass.MyTestProp = "toto 1";
Console.WriteLine(calleeClass.MyTestProp);
}
}
}
另一个问题是,如果没有在堆栈上创建testMethod中的caleeClass,那么会出现一个可以在堆栈上创建引用类型的特殊情况
答案 0 :(得分:3)
某事物是“实施细节”这一事实意味着实际的实施不是您应该知道的事情。
首先让我们处理细节:
但是这个问题实际上似乎混淆了以下两个概念:
这是两个不同的东西,对于你的问题,正确的陈述是你正在通过值传递引用类型参数。
这一切都开始让很多人感到困惑所以让我们试着看看这里发生了什么。
实际上,参考只是一个数字。它是指内存中其他对象的东西。很可能该数字是该对象的地址(在内存中)。
所以,在你构建了第一个对象之后:
MyCalleeClass calleeClass = new MyCalleeClass();
calleeClass.MyTestProp = "toto";
假设calleeClass
包含数字(引用)1234.在地址1234处,存在类型为MyCalleeClass
的对象,该对象的MyTestProp
属性具有值“toto ”
好的,然后将此引用传递给该方法。基本上,您为该方法提供参考1234 的副本。
在该方法中,您构造另一个对象并将对该对象的引用分配给相同的局部变量(参数),覆盖 1234引用,例如5678指着你的新物体。
现在你改变那个对象的属性,即新的对象。
然后返回外部代码。由于该代码为方法提供了引用1234的副本,其引用仍然具有1234并指向原始对象,在属性中带有“toto”。
这是按值传递引用的含义,您为方法提供参考值的副本。正在调用的代码仍然具有原始引用。
如果您希望调用的代码继续使用 new 引用,则需要通过引用传递引用。这也令人困惑,因为这里的两个“参考”词实际上意味着不同的东西。
TL; DR 您的代码行为方式,因为您在外部使用一个对象,并在内部构造和更改新对象,但外部世界不会回来那个新对象并继续使用旧对象。
至于你的第二个问题,可以在堆栈上分配引用类型,然后是否。所有对象都在托管堆上分配。
你可以在不安全的代码中分配堆栈上看起来像引用类型的东西,基元/值类型的数组,如下所示:
unsafe void Test()
{
int* values = stackalloc int[10];
}
但这不是“数组引用类型”,它只是指向堆栈上分配的10个int值中的第一个的指针,所以它不是一回事。
答案 1 :(得分:0)
这可以变成一个大讨论。
在TestMethod
生成MyCalleeClass
因为这是一个类(引用类型),所以它在堆上生成(比如在地址1000)
您的堆栈将包含指向地址1000的指针。
在地址1000处,您将获得对您的类中的字符串的另一个引用(字符串也是一个ref。类型,它将位于地址2000中 - 对于此示例)。
所以:
TestMethod的堆栈将有一个指向地址1000的指针 在地址1000中,您将有一个指向地址2000的指针 在地址2000中,您将有一个字符串。
答案 2 :(得分:0)
在你的主要:
callerClass.TestMethod(calleeClass) - >对calleeClass的引用被复制为参数
在你的TestMethod中
calleeClass = new MyCalleeClass - >复制的引用(方法的参数)被覆盖以引用另一个对象
当返回TestMethod调用时,不会复制引用,因此您仍在引用main中的原始类。
如果您确实需要您建议的行为,则应指定:TestMethod( ref MyCalleeClass calleeClass)。