何时复制 C# 值/对象,何时复制其引用?

Posted

技术标签:

【中文标题】何时复制 C# 值/对象,何时复制其引用?【英文标题】:When is a C# value/object copied and when is its reference copied? 【发布时间】:2011-05-19 20:58:28 【问题描述】:

在复制我要引用的对象或引用我要复制的对象的位置时,我一遍又一遍地遇到相同的问题。当我使用 = 运算符时会发生这种情况。

例如,如果我将对象发送到另一个表单,即:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

...然后修改表单中的对象,原始对象不会被修改。就好像对象被复制而不被引用一样。然而,当我这样做时:

SomeObject myObject = new SomeObject();
SomeObject anotherObject = new SomeObject();
anotherObject = myObject;

...然后修改anotherObjectmyObject 也会被修改。

最糟糕的情况是当我尝试克隆我定义的对象之一时:

public class SomeObject

    double value1, value2;

    //default constructor here

    public SomeObject(val1, val2)
    
        value1 = val1;
        value2 = val2;
    

    public void Clone(SomeObject thingToCopy)
    
        this.value1 = thingToCopy.value1;
        this.value2 = thingToCopy.value2;
    

当我这样做时......

SomeObject obj1 = new SomeObject(1, 2);
SomeObject obj2 = new SomeObject();
obj2.Clone(obj1);

...obj1 被引用,对obj2 的任何修改都会更改obj1

int, double, string 等系统对象似乎总是被复制,除了上面的克隆方法。

我的问题是,不考虑在函数中使用 ref 关键字,何时复制对象以及何时在每种情况下引用对象(即传递给函数时,设置时像其他对象一样(如上面的前两个例子),当复制成员变量时如第三个例子等)?

【问题讨论】:

【参考方案1】:

嗨,迈克 从 ValueType 派生的所有对象,例如结构或其他原始类型,都是值类型。这意味着只要将它们分配给变量或将它们作为方法参数传递,它们就会被复制。其他类型是引用类型,这意味着,当您将引用类型分配给变量时,不是它的值,而是它在内存空间中的地址分配给变量。 您还应该注意,您可以使用 ref 关键字将值类型作为引用传递。这是语法

public void MyMethod(ref int a)  a = 25 
int i = 20;
MyMethod(ref i); //Now i get's updated to 25.

希望对你有帮助:)

【讨论】:

抱歉在这里吹毛求疵。对象不派生。类型可以。对象通常是用于描述类型实例的术语。 您可以使用ref 传递参数by 引用,但这与将其传递作为 引用不同。我认为值得将两者区分开来。 @JonSkeet:我知道这是一个老问题,但你能澄清或分享一些关于你上一条评论的信息吗?我的意思是传递 by 参考与传递 as 参考?在此先感谢 :) 我一直在问,因为我一直认为两者都会以相同的结果结束,如果在外部方法中传递,这会影响传递的对象值。 @MohammedElSayed:最好只是阅读jonskeet.uk/csharp/parameters.html @JonSkeet:谢谢你的快速回复,你的下一杯咖啡在我身上;)【参考方案2】:

关于克隆对象,如果您从一个对象复制到另一个对象的值是引用类型,那么对原始对象中这些值的任何修改都会影响复制对象中的值(因为它们只是对相同对象的引用对象)

如果您需要克隆具有引用类型属性的对象,您需要使这些类型可克隆或通过根据需要实例化新实例来手动复制它们。

考虑使用IClonable 接口,虽然它不是最好的解决方案恕我直言。

【讨论】:

【参考方案3】:

如果不花很多时间仔细挑选你的话,很难准确地回答这类问题。

我在几篇您可能会觉得有用的文章中这样做了:

Parameter passing in C# / .NET Reference types and value types in C# / .NET

当然,这并不是说这些文章是完美的——远非如此——但我已经尽力做到清楚。

我认为一件重要的事情是将两个概念(参数传递和引用与值类型)分开。

查看您的具体示例:

SomeForm myForm = new SomeForm();
SomeObject myObject = new SomeObject();
myForm.formObject = myObject;

这意味着myForm.formObjectmyObject 指的是SomeObject 的同一个实例 - 就像两个人拥有不同的纸片,每张纸上都写着相同的地址。如果你去一张纸上的地址,把房子涂成红色,然后去第二张纸上的地址,你会看到一座红色的房子。

不清楚您所说的“然后修改表单中的对象”是什么意思,因为您提供的类型是不可变的。无法修改对象本身。您可以更改myForm.formObject 以引用SomeObject 的不同实例,但这就像在一张纸上草草写下地址并在上面写下不同的地址。这不会改变另一张纸上写的内容。

如果你能提供一个简短但完整的程序,你不理解它的行为(理想情况下是一个控制台应用程序,只是为了让事情更短更简单),那么具体地谈论事情会更容易条款。

【讨论】:

我将不得不写一个。我了解 C++ 中的大部分指针,我只是不明白它们何时用于 C# 中的值和引用类型,因为 C# 中没有明确的“安全”指针用法/运算符。 我将在稍后编写程序,因为我现在正在工作。感谢您的帮助。我也会阅读文章。 在不同纸张上的地址类比有助于理解引用类型的概念。 @Jon Skeet:很抱歉,当我在第二个链接(C#/.NET 中的引用类型和值类型)尝试示例时,我注意到“.Text”被排除在“ Console.WriteLine("originalPrintedPage=0",originalPrintedPage);" @Johnny_D: 什么样的类型?对于引用类型,它只是 复制引用 - 一个有效的指针。对于值类型,我相信这涉及到更多的微妙之处,但它仍然基本上是复制一块内存。它不需要使用反射——它知道类型有多大,以及值在内存中的位置。

以上是关于何时复制 C# 值/对象,何时复制其引用?的主要内容,如果未能解决你的问题,请参考以下文章

python如何决定何时按值传递参数以及何时按引用传递参数? [复制]

PHP 中的数组是作为值复制还是作为对新变量的引用,以及何时传递给函数?

C ++:在向量中插入啥 - 指针或引用,向量何时复制它的元素?

在 iOS 应用程序的生命周期中何时触发 ARC? [复制]

何时重载按引用传递(左值和右值)优于按值传递?

C# 类型基础 值类型和引用类型