C# 引用类型和引用之间的差异

C# 引用类型和引用之间的差异,c#,.net,clr,C#,.net,Clr,我正在阅读Eric Lippert的以下博客: 在这篇文章中,他提到开场白中有三种价值观: 值类型的实例 引用类型的实例 参考资料 这是不完整的。推荐信呢?引用既不是值类型,也不是引用类型的实例,而是值 因此,在以下示例中: int i = 10; string s = "Hello" 第一个是值类型的实例,第二个是引用类型的实例。那么,第三种类型是什么,引用以及我们如何获得它呢?引用类型的字段和变量,例如s,是对堆上引用类型实例的引用 您从不直接使用引用类型的实例;相反,您通过引用来使用它

我正在阅读Eric Lippert的以下博客:

在这篇文章中,他提到开场白中有三种价值观:

  • 值类型的实例

  • 引用类型的实例

  • 参考资料

    这是不完整的。推荐信呢?引用既不是值类型,也不是引用类型的实例,而是值

  • 因此,在以下示例中:

    int i = 10;
    string s = "Hello"
    

    第一个是值类型的实例,第二个是引用类型的实例。那么,第三种类型是什么,引用以及我们如何获得它呢?

    引用类型的字段和变量,例如
    s
    ,是对堆上引用类型实例的引用

    您从不直接使用引用类型的实例;相反,您通过引用来使用它

    那么,第三种类型是什么,引用,我们如何获得它

    变量
    s
    是一个保存引用值的变量。此值是对内存中字符串的引用(值为
    “Hello”

    为了更清楚地说明这一点,假设您有:

     string s1 = "Hello";
     string s2 = s1;
    

    在这种情况下,
    s1
    s2
    都是变量,每个变量都是对同一引用类型实例(字符串)的引用。这里只涉及一个实际的字符串实例(引用类型),但有两个对该实例的引用。

    引用实际上不是“第三种类型”。它实际上是一个指针,指向一个对象的具体实例。看看这个例子:

    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中,该实例的值被覆盖。

    最后一个示例是MyClass的对象。所有类都是引用类型,这就是为什么您总是将它们作为引用传递(不需要特殊的关键字)

    您可以将引用视为计算机内存中的地址。位于该地址的字节构成对象。如果您将其作为值传递,则将该字节取出并传递给函数。如果将其作为引用传递,则只传递地址。而在被调用的函数中,您可以从该地址读取字节或写入该地址。每次更改都会影响调用函数变量,因为它们指向计算机内存中完全相同的字节这不完全是.Net中发生的事情(它在虚拟机中运行),但我认为这个类比将帮助您理解这个概念

    我们为什么要使用参考资料?原因有很多。其中之一是,按值传递大对象的速度非常慢,需要对其进行克隆。当您传递一个对象的引用时,不管该对象有多大,您只传递内存中包含其“地址”的几个字节

    此外,对象可能包含无法克隆的元素(如打开的套接字)。使用引用,您可以轻松地在函数之间传递这样的对象


    还值得一提的是,scstruct,尽管它们看起来非常类似于类,但实际上是值类型,并且表现为值类型(当您将结构传递给函数时,实际上是传递了一个克隆-一个新实例)。

    @marcGravel,Eric Lippert的
    s
    是一个变量,而不是链接中的引用:“引用既不是值类型,也不是引用类型的实例,而是值。”。“@NewHire,而这正是他们普遍存在的地方。因此,虽然从语言规范来看这不是必要的,但它在事实上仍然是准确的。@用户确实,说“s的值”更合适。@NewHire:理论上,CLR可以在堆栈上分配引用类型的引用对象,但在实践中,这种情况从未发生过。但是,根据保存引用的变量的生存期,可以在堆栈或堆上分配引用。参考文献是数据,它们必须生活在某个地方。