我在C#的pass-by-reference-pass-by-value-concept的上下文中有一个关于深度和浅拷贝的一般性问题:
在C#中,需要显式创建接受指针/引用的方法,以便能够将此方法传递给方法。但是,至少作为参数传递给方法/构造函数的对象的行为与其他对象不同。如果没有按照此处所述进行额外克隆,它们似乎总是通过引用传递:http://zetcode.com/lang/csharp/oopii/。
为什么对象会通过引用自动传递? 在这些情况下,强制克隆过程是否有任何特别的好处,而不是更像int,double,boolean等处理对象?
以下代码示例说明了我的意思:
using System;
public class Entry
{
public class MyColor
{
public int r = 0;
public int g = 0;
public int b = 0;
public double a = 1;
public MyColor (int r, int g, int b, double a)
{
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
}
public class A
{
public int id;
public MyColor color;
public MyColor hiddenColor;
public A (int id, MyColor color)
{
this.id = id;
this.color = color;
}
}
static void Main(string[] args)
{
int id = 0;
MyColor col = new MyColor(1, 2, 3, 1.0);
A a1 = new A(id, col);
A a2 = new A(id, col);
a1.hiddenColor = col;
a2.hiddenColor = col;
a1.id = -999;
id = 1;
col.a = 0;
Console.WriteLine(a1.id);
Console.WriteLine(a2.id);
Console.WriteLine(a1.color.a);
Console.WriteLine(a2.color.a);
Console.WriteLine(a1.hiddenColor.a);
Console.WriteLine(a2.hiddenColor.a);
}
}
这导致:
-999
0
0
0
0
MyCol
的实例始终通过引用传递,而其他参数按值传递。我必须在类MyColor
和A
中实现ICloneable。另一方面,'#'语句存在于C#中,应该用于明确允许和传递引用。
建议欢迎!
答案 0 :(得分:35)
为什么对象会通过引用自动传递?
他们不是。
在这些情况下,强制克隆过程是否有任何特别的好处,而不是更像int,double,boolean等处理对象?
没有"克隆过程"对于引用类型,仅适用于值类型。
我认为你混淆了不同的概念:
值类型与参考类型
对于值类型(例如原始数字类型,枚举和DateTime
等结构),变量的值是对象本身。将变量分配给另一个(或通过值将其作为参数传递)会创建对象的副本。
对于引用类型(例如object
,string
,类(非结构)等),变量的值是对象的引用。将变量分配给另一个(或通过值将其作为参数传递)会创建引用的副本,因此它仍然引用相同的对象实例。
按值与参考
传递参数按值传递参数意味着您传递值的副本。根据它是值类型还是引用类型,这意味着对象本身的副本或引用的副本。如果被调用者修改作为参数传递的值类型的成员,则调用者不会看到更改,因为被调用者正在处理副本。另一方面,如果被调用者修改作为参数传递的引用类型的成员,则调用者将看到更改,因为被调用者和调用者都具有对同一对象实例的引用。
通过引用传递参数意味着您将引用传递给变量(可以是值类型或引用类型的变量)。该值不会被复制:它在调用者和被调用者之间共享。因此,调用者可以看到被调用者所做的任何更改(包括为参数赋值)。
除非另有说明(使用ref
或out
关键字),否则所有参数都按值传递,包括引用类型。对于引用类型而言,传递的值是引用,但它仍然按值传递。
我建议您阅读Jon Skeet的文章Parameter passing in C#以获得更好的解释。
答案 1 :(得分:3)
除非您明确指定应使用ref
或out
关键字通过引用传递它们,否则所有方法参数都按值传递。这意味着如果将变量传递给方法参数,则会复制变量的内容并将其传递给方法。
如果变量是值类型,这基本上意味着struct
,则变量包含一个对象,因此该对象被复制。如果变量是引用类型,这基本上意味着class
,则变量包含对对象的引用,以便复制引用。
如果您将参数声明为ref
或out
,则会创建对该变量的引用,并将其传递给该方法。如果变量包含一个对象,则创建对该对象的引用,如果该变量包含引用,则创建对该引用的引用。
答案 2 :(得分:2)
我会重新提出你的问题:为什么我们需要上课?我们不能只有结构吗?
并非所有对象都可以安全复制。例如,您无法在逻辑上复制FileStream
或Button
。这些对象具有标识,并且您希望所有代码都引用唯一的对象。
答案 3 :(得分:0)
类或接口类型的变量,参数或字段(统称为"引用类型")不包含类对象;它包含一个对象标识符。同样,引用类型的数组也不包含对象;它包含对象标识符。
尽管.NET中的对象没有任何与它们相关联的人类可读标识符,但如果它们这样做可能有助于推理它们:如果在课程期间创建了至少一些数字(例如592)个对象一个程序的执行,只有一个对象将是第592个创建的对象;一旦创建了第592个对象,其他任何对象都不会是第592个。没有办法找出哪个对象是592nd,但如果一个保存对592nd对象的引用的变量作为非ref参数传递给某个方法,它将继续保持对第592个的引用方法返回时的对象。如果对象#592是对Car
的实例的引用,其为红色,则局部变量myCar
包含"对象ID#592",并且一个调用方法{{1然后该方法将收到PaintCar(myCar);
。如果该方法将汽车涂成蓝色,那么当它返回时,Object #592"
将保持"对象#592",这将识别蓝色汽车。
答案 4 :(得分:-3)
ok ill编辑:我从以下文章中得到纠正
嗯,我想我不擅长解释事情。