gpt4 book ai didi

c# - 通过 ref 和没有 ref 传递的引用类型

转载 作者:行者123 更新时间:2023-11-30 19:52:48 29 4
gpt4 key购买 nike

我以不同的方式调用 4 种方法时得到不同的结果:

static void Main(string[] args)
{
var emp = new Employee { Name = "ABC" };

ChangeName1(emp);
Console.WriteLine(emp.Name); //XYZ

ChangeName2(ref emp);
Console.WriteLine(emp.Name); //XYZ

ChangeToNull1(emp);
Console.WriteLine(emp.Name); // XYZ

ChangeToNull2(ref emp);
Console.WriteLine(emp.Name); // Null Reference Exception

Console.ReadLine();
}

static void ChangeName1(Employee e)
{
e.Name = "XYZ";
}
static void ChangeName2(ref Employee e)
{
e.Name = "XYZ";
}
static void ChangeToNull1(Employee e)
{
e = null;
}
static void ChangeToNull2(ref Employee e)
{
e = null;
}

如您所见,前 2 个方法,我正在更改 Name 属性的值,从该方法返回后,原始对象属性已更改。

但是当将对象设置为null时,ChangeToNull1 方法不会更改原始对象,但ChangeToNull2 方法会。

所以我的问题是:

1. 为什么 C# 会这样?

2. C# 是否在传递给方法时复制原始对象?

3. 如果是,那么它如何更改原始对象属性,如 Name,为什么不更改原始对象?

4. ref 传递时,c# 是否复制原始对象?

最佳答案

在 C# 中,有两类对象:值类型和引用类型。

值类型是structenum,例如int (System.Int32)。这些类型在传递时总是被复制。如果您更改方法中的 int,调用方内部的变量将不会更改。

你说的是引用类型——基本上是类、数组和接口(interface)。

在引用类型中,例如string (System.String),有两个部分:对象和指针。例如,让我们看看您的 Employee。假设我声明了一个名为 e1 且类型为 Employee 的变量,并为其分配了名称 "abc":

Employee e1 = new Employee { Name = "abc" };

现在,内存(堆,因为引用类型几乎总是在堆中分配,期望stackalloc)中有一个employee对象,其中包含Name="abc"。还有(在堆栈中)指向此对象的指针。假设我们有这个内存图像:

 0x123 - An employee object          0x45F - the variable `e1` - pointer to the employee|-------------------------|         |------------------||     Name = "abc"        |         |     0x123        ||-------------------------|         |------------------|

When you pass it without ref, the value of e1 - 0x123 is copied, but the employee isn't copied! So, if you change its name, the original employee will be changed! But if you change the pointer nothing will happen, since the pointer e1 is just copied!

When you pass with ref, the address of the pointer is copied - 0x45F. So, if you change the parameter, it will change e1, since it was not copied, but its address.

Edit:

If I assign a reference type variable to an another variable, for example:

var e1 = new Employee { Name = "abc" };
Employee e2 = employee;

然后,e2e1 相同 - 它也是一个指针,指向相同的地址。如果我们采用之前的内存镜像,现在在地址 0x4AC 中有一个名为 e2 的变量,还包含对象的地址 0x123 .因此,如果我们更改 e2.Name:

e2.Name = "new";

然后,e1.Name 现在也是 “new”。关于引用类型的最后一个重要事实是,比较 (==) 引用类型(我说的是没有重载运算符 ==),不会检查如果它们包含相同的值,但如果它们指向相同的对象。让我们看一个例子:

var e1 = new Employee { Name = "abc" };
Employee e2 = e1;
var e3 = new Employee { Name = "abc" };
var e4 = new Employee { Name = "123" };

Console.WriteLine(e1 == e4); // false
Console.WriteLine(e1 == e3); // false, since they don't point to the same object, they just contain the same values
Console.WriteLine(e1 == e2); // true, since they point to the same object

关于字符串的一些注释:

  1. 尽管 System.String 类是引用类型,但它重载了 operator ==(),因此比较两个字符串将给出正确的结果。
  2. 编译器通常会优化字符串,所以如果 string s1 = "abc"; and string s2 = "abc";s1 and s2 可以指向同一个地址(为了节省内存)。这是可能的,因为字符串是不可变的 - 例如,如果您对字符串调用 Replace(),它将创建一个新字符串。所以你不应该知道这一点(但如果你编写了不安全的代码,这很重要)。

关于c# - 通过 ref 和没有 ref 传递的引用类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53369231/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com