C# 为什么对象会自动通过引用传递?
在C#的按引用传递和按值传递概念的背景下,我有一个关于深度和浅层复制的一般性问题: 在C#中,需要显式创建接受指针/引用的方法,以便能够将这些指针/引用传递给方法。但是,至少作为参数传递给方法/构造函数的对象的行为与其他对象不同。如果没有按照此处所述进行额外的克隆,则它们似乎总是通过引用传递: 为什么对象会自动通过引用传递? 在这些情况下,强制执行克隆过程而不是像int、double、boolean等那样处理对象有什么特别的好处吗? 下面的代码示例说明了我的意思:C# 为什么对象会自动通过引用传递?,c#,clone,pass-by-reference,pass-by-value,shallow-copy,C#,Clone,Pass By Reference,Pass By Value,Shallow Copy,在C#的按引用传递和按值传递概念的背景下,我有一个关于深度和浅层复制的一般性问题: 在C#中,需要显式创建接受指针/引用的方法,以便能够将这些指针/引用传递给方法。但是,至少作为参数传递给方法/构造函数的对象的行为与其他对象不同。如果没有按照此处所述进行额外的克隆,则它们似乎总是通过引用传递: 为什么对象会自动通过引用传递? 在这些情况下,强制执行克隆过程而不是像int、double、boolean等那样处理对象有什么特别的好处吗? 下面的代码示例说明了我的意思: using System;
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
mycl
的实例始终通过引用传递,而其他参数则通过值传递。我必须在类MyColor
和A
中实现iClonable。另一方面,“ref”语句出现在C#中,它应该用于显式地允许并通过引用传递
欢迎您的建议
为什么对象会自动通过引用传递
他们不是
在这些情况下,强制执行克隆过程而不是像int、double、boolean等那样处理对象有什么特别的好处吗
引用类型没有“克隆过程”,只有值类型
我认为你混淆了不同的概念:
- 值类型与引用类型
对于值类型(如基本数字类型、枚举和结构,如
),变量的值是对象本身。将变量指定给另一个(或通过值将其作为参数传递)将创建对象的副本 对于引用类型(如DateTime
、对象
、类(非结构)等),变量的值是对对象的引用。将变量指定给另一个(或通过值将其作为参数传递)将创建引用的副本,因此它仍然引用同一个对象实例字符串
- 通过值传递参数与通过引用传递参数
按值传递参数意味着传递值的副本。根据它是值类型还是引用类型,这意味着对象本身的副本,还是引用的副本。如果被调用方修改作为参数传递的值类型的成员,调用方将看不到更改,因为被调用方正在处理副本。另一方面,如果被调用方修改作为参数传递的引用类型的成员,调用方将看到更改,因为被调用方和调用方都有对同一对象实例的引用
通过引用传递参数意味着将引用传递给变量(可以是值类型或引用类型的变量)。不复制该值:它在调用方和被调用方之间共享。因此,被调用方所做的任何更改(包括为参数赋值)都将被调用方看到
除非另有规定(使用
或ref
关键字),否则所有参数均按值传递,包括引用类型。只是对于引用类型,传递的值是一个引用,但它仍然是通过值传递的out
我建议您阅读Jon Skeet的文章以获得更好的解释。所有方法参数都是按值传递的,除非您明确指定它们应该使用
ref
或out
关键字按引用传递。这意味着,如果将变量传递给方法参数,则会复制变量的内容并将其传递给方法
如果变量是值类型,基本上意味着结构,那么该变量包含一个对象,因此该对象被复制。如果变量是引用类型,这基本上意味着类
,那么该变量包含对对象的引用,以便复制引用
如果将参数声明为
ref
或out
,则会创建对变量的引用,并将其传递给方法。如果变量包含对象,则创建对该对象的引用;如果变量包含引用,则创建对该引用的引用。我将重新表述您的问题:我们为什么需要类?我们就不能只有结构吗
并非所有对象都可以安全复制。例如,您不能从逻辑上复制
文件流
或按钮
。这些对象具有标识,您希望所有代码都引用唯一的对象。类或接口类型(统称为“引用类型”)的变量、参数或字段不包含类对象;它包含一个对象标识符。同样,引用类型的数组不包含对象;它保存对象标识符
即使.NET中的对象没有任何与之相关联的人类可读标识符,也可以像这样对它们进行推理:如果在程序执行过程中至少创建了一些对象(例如592个),那么恰好有一个对象将是创建的第592个对象;一旦创建了第592个对象,就不会有其他对象成为第592个对象。没有办法找出哪个对象是592,但是如果保存对592对象的引用的变量作为非引用参数传递给某个方法,那么当该方法返回时,它将继续保存对592对象的引用。如果对象#592是对红色的Car
实例的引用,则局部变量myCar
保存“objectid#592”,并调用方法PaintCar(myCar)
,则该方法将接收对象#592“
。如果该方法将汽车漆成蓝色,则当它返回时,myCar
将保留“Object#592”,这将识别