引用并不是真正的“第三种类型”。它实际上是一个指向对象的具体实例的指针。看看这个例子:
class MyClass
{
public string Str { get; set; }
}
class Program
{
static void Main(string[] args)
{
int a = 1;
int b = 2;
int c = 3;
var myObj = new MyClass
{
Str = "Whatever"
};
Console.WriteLine("{0};\t{1};\t{2};\t{3}", a, b, c, myObj.Str);
MyFunction(a, ref b, out c, myObj);
Console.WriteLine("{0};\t{1};\t{2};\t{3}", a, b, c, myObj.Str);
Console.ReadLine();
}
static void MyFunction(int justValue, ref int refInt, out int outInt, MyClass obj)
{
obj.Str = "Hello";
justValue = 101;
refInt = 102;
outInt = 103; // similar to refInt, but you MUST set the value of the parameter if it's uses 'out' keyword
}
}
这个程序的输出是:
1; 2; 3; Whatever
1; 102; 103; Hello
关注 MyFunction:
我们传递的第一个参数是一个简单的 int,它是一个值类型。默认情况下,值类型在作为参数传递时会被克隆(正在创建新实例)。这就是'a'的值没有改变的原因。
您可以通过向参数添加“ref”或“out”关键字来更改此行为。在这种情况下,您实际上传递了对您的 int 实例的引用。在 MyFunction 中,该实例的值被覆盖。
Here you can read move out ref and out
最后一个例子是 MyClass 的对象。所有类都是引用类型,这就是为什么您总是将它们作为引用传递(不需要特殊关键字)。
您可以将引用视为计算机内存中的地址。该地址的字节组成您的对象。如果将其作为值传递,则将这些字节取出并将它们传递给函数。如果您将其作为参考传递,则仅传递地址。比在您调用的函数中,您可以从该地址读取字节或写入该地址。每个更改都会影响调用函数变量,因为它们指向计算机内存中完全相同的字节。 这与 .Net 中的情况不同(它在虚拟机中运行),但我认为这个类比会帮助您理解这个概念。
我们为什么要使用引用?有很多原因。其中之一是通过值传递一个大对象会非常慢并且需要克隆它。当您传递对对象的引用时,无论该对象有多大,您都只会传递 w 几个字节,其中包含它在内存中的“地址”。
此外,您的对象可能包含无法克隆的元素(如打开的套接字)。使用引用,您可以轻松地在函数之间传递这样的对象。
还值得一提的是结构体,尽管它们看起来与类非常相似,但它们实际上是值类型并且表现为值类型(当您将结构体传递给函数时,实际上是传递了一个克隆 - 一个新实例)。