我正在阅读Eric Lippert的博客:The truth about Value types
在此,他提到开幕式中有三种价值观:
价值类型实例
参考类型实例
参考
不完整。参考怎么样?引用既不是值类型也不是引用类型的实例,但它们是值..
因此,在以下示例中:
int i = 10;
string s = "Hello"
首先是值类型的实例,第二个是引用类型的实例。那么,第三种类型是什么,参考文献以及我们如何获得它?
答案 0 :(得分:4)
那么,第三种类型是什么,参考文献以及我们如何获得它?
变量s
是一个保存引用值的变量。此值是对内存中字符串(值为"Hello"
)的引用。
为了使这一点更清楚,请说:
string s1 = "Hello";
string s2 = s1;
在这种情况下,s1
和s2
都是变量,每个变量都是对同一引用类型实例(字符串)的引用。这里只涉及一个实际的字符串实例(引用类型),但是有两个对该实例的引用。
答案 1 :(得分:3)
引用类型的字段和变量(例如s
)是对存在于堆上的引用类型实例的引用。
您永远不会直接使用引用类型的实例;相反,你通过参考使用它。
答案 2 :(得分:0)
参考不是真正的第三种类型'。它实际上是一个指向对象的具体实例的指针。看一下这个例子:
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(它在虚拟机中运行)中发生的事情,但我认为这个类比将帮助您理解这个概念。
为什么我们使用引用?原因很多。其中之一是按值传递一个大对象会非常慢并且需要克隆它。当您传递对象的引用时,无论该对象有多大,您只需传递包含它的几个字节的地址'在记忆中。
此外,您的对象可能包含无法克隆的元素(如打开的套接字)。使用引用,您可以轻松地在函数之间传递这样的对象。
值得一提的是,即使它们看起来非常类似于类,它们实际上是值类型并且表现为值类型(当您将结构传递给函数时,实际上您传递了一个克隆 - 一个新实例)。