为什么在复制之后原始对象发生了变化,而不使用ref参数?

Cuc*_*ser 3 c# asp.net reference

在工作中,我们遇到了一个问题,即在通过方法发送副本后原始对象发生了变化.我们确实通过IClonable在原始类中使用找到了解决方法,但是因为我们无法找到它首先发生的原因.

我们编写了这个示例代码来重现问题(类似于我们的原始代码),并希望有人能够解释它为什么会发生.

public partial class ClassRefTest : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var myclass = new MyClass();
        var copy = myclass;
        myclass.Mystring = "jadajadajada";
        Dal.DoSomeThing(copy);

        lit.Text = myclass.Mystring; //Text is expected to be jadajadajada, 
                                       but ends up to be referenced
    }
}

public class MyClass
{
    public string Mystring { get; set; } 
}

public static class Dal
{
    public static int? DoSomeThing(MyClass daclass)
    {
        daclass.Mystring = "referenced";
        return null;
    }

}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,在DoSomething()方法中我们没有使用任何ref参数,但lit.Text最终还是要引用.

为什么会这样?

Ste*_*eve 8

解释这是如何工作总是很有趣.当然,我的解释不能与Jon SkeetJoseph Albahari的辉煌相提并论,但我会尝试.

在C编程的旧时代,掌握指针的概念是使用该语言的基础.这么多年过去了,现在我们称之为引用,但它们仍然是......美化的指针,如果你了解它们是如何工作的,你就成了程序员的一半(开玩笑)

什么是参考?我会告诉你一个非常简短的回答.它是存储在变量中的数字,该数字表示数据所在的内存中的地址.
为什么我们需要参考?因为处理单个数字非常简单,我们可以使用它来读取数据的内存区域,而不是将整个对象及其所有字段与我们的代码一起移动.

那么,当我们写作时会发生什么

var myclass = new MyClass();
Run Code Online (Sandbox Code Playgroud)

我们都知道这是对类的构造函数的调用MyClass,但对于Framework,它也是一个提供内存区域的请求,其中实例的值(属性,字段和其他内部管理信息)存在并存在于具体时间点.假设MyClass需要100个字节来存储它需要的一切.框架以某种方式搜索计算机内存,并假设它在由地址4200标识的内存中找到一个位置.该值(4200)是它分配给var myclass它的值它是一个指向内存的指针(哎呀它是对象实例的引用)

现在打电话会发生什么?

var copy = myclass;
Run Code Online (Sandbox Code Playgroud)

没什么特别的.的copy变量获得的相同的值myclass(4200).但是这两个变量引用了相同的内存区域,因此使用其中一个或另一个没有任何区别.内存区域(MyClass的实例)仍然位于我们的虚构内存地址4200.

myclass.Mystring = "jadajadajada";
Run Code Online (Sandbox Code Playgroud)

它使用引用值作为基值来查找属性占用的内存区域,并将其值设置为保留文字字符串的实习区域.如果我可以用指针进行类比,就像你取基本内存(4200)一样,添加一个偏移量来找到表示属性MyString的引用保持在我们的对象实例占用的100字节边界内的点.假设MyString引用超过了内存区域的开头42个字节.添加42到4200个yelds 4242,这就是存储对文字"jadajadajada"的引用的点.

Dal.DoSomeThing(copy);
Run Code Online (Sandbox Code Playgroud)

这里的问题(你有问题的地方).传递copy变量时,不要认为框架重复搜索内存区域并复制新区域中原始区域的所有内容.不,这几乎是不可能的(想想MyClass是否包含属性是另一个类的实例等等......它永远不会停止.)因此传递给该DoSomeThing方法的值也是参考值4200.这个值被自动分配给daclass声明为输入参数的局部变量DoSomething(就像你之前明确做过的那样)var copy = myclass;.

此时很明显,任何操作都使用daClass原始实例占用的相同内存区域上的操作,并且当代码返回到起始点时,您会看到结果.

请原谅技术专家的用户请原谅.特别是对于我对"记忆地址"一词的随意和不精确的使用.