【问题标题】:Value and Reference types confusion值和引用类型混淆
【发布时间】:2017-08-09 11:47:03
【问题描述】:

我在每个关于这个主题的网站上都在阅读过去几天 Jon Skeets 关于 References and ValuesParameter passing 的文章。我理解了这两种类型的概念,它们代表什么以及与 Value 和 Reference 参数的区别是另一个规范。

我了解它是如何工作的以及如何使用它,但我不明白大家对这个话题的看法。你们都说,例如int 是一个值类型,string 是一个引用类型。但根据我的理解,类型基本上只取决于声明它们的类的类型。

看看这段代码 sn-p 来理解我的意思:

public struct IntHolder
{
   public int number;
}

IntHolder first = new IntHolder();
first.number = 5;
IntHolder second = first;
first.number = 6;

现在second.number 的值为5。如果将struct 更改为classnumber 将充当引用类型,因此int 是一个值类型并不重要,它只是关于类的类型。同样的例子也适用于string 等...。

显然,声明对象的类的类型设置了其对象的类型,否则我误解了一个核心概念。请纠正我并帮助我正确理解它或说出int string 等...具有特殊类型的含义,即使它们何时被初始化并不重要,所以基本上总是如此。

【问题讨论】:

  • IntHolder second = first;创建了一个新结构,而不是对第一个结构的引用。同样通过引用将参数传递给方法与引用/值类型的区别没有任何关系
  • "现在 second.number 的值为 5。如果将 struct 更改为 class,则 number 将充当引用类型" - 不,这根本不是真的。如果将结构更改为类,IntHolder 将是引用类型,但 int 仍将是值类型。
  • 好吧,如果我的结论有误,我至少知道一个。因此,当我将IntHolder 分配为引用类型并稍后执行此操作时,IntHolder second = first; 第二个将是对第一个的引用,即对创建的 IntHolder 对象的引用。因此,当我首先使用引用更改对象的值时,它会自动与第二个指向的对象的值相同,因为它是相同的。正确的?但是,例如int 的类型有什么关系呢?
  • 使用引用类型执行second = first复制引用secondfirst 无关,只是它是变量的精确副本,是指向同一对象的引用。

标签: c# value-type reference-type


【解决方案1】:

这与您要更改的字段类型无关。这里相关的确实只是父类型的类型:

IntHolder first = new IntHolder();
IntHolder second = first;

根据IntHolder 的种类,这有两种不同的效果:

对于值类型 (struct),这会创建一个副本。值类型对象的数据对象一起存在,所以它全部被复制。这有点等价于:

IntHolder second = new IntHolder();
second.number = first.number;
// and all other fields (visible or not) are copied too

这意味着对 已复制 值类型的字段的赋值将覆盖该值而不影响原始对象。这就像一个局部变量的行为方式:

var x = 5;
var y = 2;
y = 3; // this does not change x

但是,当类型是引用类型时,赋值second = first 只是复制引用。保留值的基础对象对于两者都是相同的。因此,任何一个对象的更改都会影响另一个对象——因为没有“其他”:它只是被两个单独的变量引用的同一个对象。


回答评论中的后续问题:

我如何想象int 变量是值类型而string 是引用类型?我的意思是,int 变量直接包含数字,而string 变量就像一个指向字符串对象存储位置的指针?

是的,就是这样。引用的值基本上只是指向对象实际所在内存的指针,而值类型的值是整个对象本身。

这就是为什么例如将值类型作为方法的参数意味着在调用该方法时,将整个值类型对象复制到堆栈中以执行该方法。

类型是值类型并不意味着它的成员也都是值类型。例如,值类型中字符串成员的实际存储值仍然是对字符串对象的引用。类似地,引用类型的实际内存将包含值类型的实际值,以及对引用类型的其他内存位置的引用。

如果父类型总是比对象类型更重要,我可以将 int 和 string 类型用于什么?

没有什么比另一个更重要。作为值或引用类型的对象仅影响该对象的存储方式。对于对象的成员,这是完全分开评估的。

如果您只有一个类和一堆变量,是否可以将其中一些设置为对另一个变量的引用,例如int 变量。

你不能有一个值类型的引用指针,就像你在 C 中一样,不。但是您可以引用允许您更改值类型字段的值的字段。例如:

class Test
{
    private int field;

    public void ShowExample()
    {
        // set the value
        field = 12;

        // call the method and pass *a reference* to the field
        // note the special syntax
        MutateField(ref field);

        // the field, which is a value type, was mutated because it was passed as a reference
        Console.WriteLine(field == 4);
    }

    private static void MutateField(ref int value)
    {
        value = 4;
    }
}

【讨论】:

  • 好的,谢谢,但我还有几个问题。第一:如果你只有一个类和一堆变量,是否有可能将其中一些设置为对另一个变量的引用,例如int 变量。接下来,如果父类型总是比对象类型更重要,我可以使用intstring 的类型做什么?最后一件事是我如何想象int 变量是值类型而string 是引用类型。我的意思是int变量直接包含数字,而string变量就像一个指向字符串对象存储位置的指针?
  • @L.Guthardt 扩展了我对这些后续问题的回答。
  • 哇,非常感谢您的详细回答。 Unfortenately 我不能给你更多的声誉。我只剩下一个问题了,那就别管你了。 :) 当我执行一个方法并且该方法在开始时获取一个参数时,无论这是一个值还是引用类型变量(比如intstring),该方法的给定参数只是一个版权?这就是为什么我不能在这个方法中改变对象本身,即使变量是引用类型。因为参数只是这个对象的一个​​副本,如果我不用ref标记它。
  • 不,当你将一个对象传递给一个方法时,你基本上和你做var parameterValue = value时的效果是一样的。所以对于值类型,该方法接收一个独立的副本,而对于引用类型,该方法接收一个引用的副本,所以 mutating 对象也会改变原始对象。这就是为什么我可以通过例如一个方法列表,并让该方法为我填写列表。传递ref 时,您实际上传递了对变量/字段的引用,并且能够更改那个(而不仅仅是值)。
  • 当您执行inputParameter = "modified text" 时,您不会更改传递的字符串对象的值。您正在用对该新字符串对象的引用替换对传递的字符串对象的引用。因此,您将无法访问最初传递给该方法的字符串对象。字符串通常也是不可变的,因此一旦创建字符串就无法更改它。如果有 String.AppendCharacter 或其他内容,那么您会看到与更改列表时相同的内容。
猜你喜欢
  • 2015-05-13
  • 2013-12-31
  • 1970-01-01
  • 1970-01-01
  • 2011-11-04
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 2018-07-17
相关资源
最近更新 更多