【问题标题】:Difference between Reference Types and References引用类型和引用之间的区别
【发布时间】:2014-03-26 19:55:34
【问题描述】:

我正在阅读 Eric Lippert 的以下博客:The truth about Value types

在此,他在开头提到了 3 种价值观:

  1. 值类型的实例

  2. 引用类型的实例

  3. 参考文献

    这是不完整的。参考文献呢?引用既不是值类型也不是引用类型的实例,但它们是值..

所以,在下面的例子中:

int i = 10;
string s = "Hello"

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

【问题讨论】:

    标签: c# .net clr


    【解决方案1】:

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

    变量s 是一个保存引用值的变量。该值是对内存中字符串(值为"Hello")的引用。

    为了更清楚,假设你有:

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

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

    【讨论】:

      【解决方案2】:

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

      你永远不会直接使用引用类型的实例;相反,您可以通过引用来使用它。

      【讨论】:

      • @MarcGravell,“选择使用堆栈还是堆从根本上与所存储事物的类型有关,这完全是错误的” - 来自 Eric Lippert 的 blog
      • s 是一个变量,而不是链接中的引用:“引用既不是值类型也不是引用类型的实例,但它们是值。”
      • @NewHire 但这是他们普遍存在的地方。因此,虽然这不是语言规范的必要条件,但它实际上是准确的。
      • @user 确实,更恰当的说法是“s
      • @NewHire:理论上,CLR 可以在堆栈上分配引用类型的 referent,但实际上这永远不会发生。但是,reference 可能会分配在堆栈或堆上,具体取决于保存引用的变量的生命周期。引用是数据,它们必须存在于某个地方。
      【解决方案3】:

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

      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 几个字节,其中包含它在内存中的“地址”。

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

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

      【讨论】:

        猜你喜欢
        • 2013-05-19
        • 1970-01-01
        • 1970-01-01
        • 2014-08-27
        • 2018-08-02
        • 1970-01-01
        • 1970-01-01
        • 2013-12-12
        • 1970-01-01
        相关资源
        最近更新 更多