为了回答您的问题,让我们先来看一下ValueTypes。ValueType保存值。也就是说,它不会指向另一个保存值的内存位置,而是它的内存位置就是该值。
因此,
int i = 10;
int j = i;
这里发生的是将i的值的副本赋给j。它们都有相同的值,但它们在内存中是不同的位置。换句话说,每次将valuetype分配给另一个valuetype时,都会进行复制。
与之相反的是ReferenceTypes。
object o = 10;
object p = o;
因为o是ReferenceType,所以o指向一个保存值为10的内存位置(实际上它是装箱的,但我会保持简单)。在下一行中,p现在指向相同的内存位置。换句话说,引用类型有两个要素:
1.地址指针
2.实际“物体”所在的内存位置(该地址指向)。
如果您已经理解到这里,那么我们可以继续探讨按值传递和按引用传递。
在C#中,参数是按值传递的。因此,如果您将valueType传递给期望valuetype参数的方法,则:
int i = 10;
SomeMethod(i);
Console.WriteLine(i);
static void SomeMethod(int value)
{
value = 20;
}
当调用SomeMethod方法时,i的值的副本被发送到该方法。如果该方法操作参数,则不会影响原始变量i。因此,在控制台窗口中看到的是10;
与引用类型相比,这种情况是不同的;
class Program
{
static void Main(string[] args)
{
Customer c = new Customer() { Name = "Mike" };
SomeMethod(c);
Console.WriteLine(c.Name);
}
static void SomeMethod(Customer customer)
{
customer.Name = "John";
}
}
class Customer
{
public string Name { get; set; }
}
由于c是引用类型,而C#按值传递参数。因此传递的是引用值的“值”的副本。也就是说,传递了c指向的地址的值。在方法中,由于地址相同(虽然是副本,但指向同一内存位置),方法能够操作对象的状态。因此,在控制台窗口中看到的是“John”,而不是“Mike”。
但是,如果该方法尝试将另一个实例分配给参数(在这种情况下称为“customer”),那么情况就会改变。
class Program
{
static void Main(string[] args)
{
Customer c = new Customer() { Name = "Mike" };
SomeMethod(c);
Console.WriteLine(c.Name);
}
static void SomeMethod(Customer customer)
{
customer = new Customer();
customer.Name = "John";
}
}
class Customer
{
public string Name { get; set; }
}
请注意,在该方法中,我们创建了一个名为Customer的新实例,并将其分配给参数customer,然后将此新实例的名称设置为“John”。在控制台窗口中,我们将看到“Mike”,而不是john。
这是因为在将变量(c)传递给方法之前,复制了原始变量的副本。现在在方法中,我们有另一个地址,然后操作该新地址,因此原始实例保持不变。明白了吗?
好的,如果这有意义。那么,如果我们确实希望SomeMethod能够执行我们尝试执行的操作呢?那么,参数不能按值传递,而必须按引用传递。这意味着变量c和两个部分(它所指向的地址的值和地址本身)被传递。因此,现在您正在按引用传递引用类型。
class Program
{
static void Main(string[] args)
{
Customer c = new Customer() { Name = "Mike" };
SomeMethod(ref c);
Console.WriteLine(c.Name);
}
static void SomeMethod(ref Customer customer)
{
customer = new Customer();
customer.Name = "John";
}
}
class Customer
{
public string Name { get; set; }
}