C# 4.0 字符串类型的属性的传递方式

C# 4.0 字符串类型的属性的传递方式,c#-4.0,properties,C# 4.0,Properties,我有以下代码(注意下面的代码不会更新属性) 及 现在我意识到我需要更改第二个方法,以便它返回一个字符串并手动将其分配给第一个示例中的属性 我不确定的是,当我将ref类型指定为方法的实际参数时,它在堆栈上的值的副本(即它在堆中的内存地址)被复制到堆栈上方法形式参数的新位置,因此它们都指向堆上的相同内存地址。因此,当我更改formal参数的值时,它实际上会更改存储在堆上的值,从而更改实际的参数值 显然,我遗漏了一些东西,因为我必须返回一个字符串并手动将其分配给属性。如果有人能指出我的误解,我将不胜感

我有以下代码(注意下面的代码不会更新属性)

现在我意识到我需要更改第二个方法,以便它返回一个字符串并手动将其分配给第一个示例中的属性

我不确定的是,当我将ref类型指定为方法的实际参数时,它在堆栈上的值的副本(即它在堆中的内存地址)被复制到堆栈上方法形式参数的新位置,因此它们都指向堆上的相同内存地址。因此,当我更改formal参数的值时,它实际上会更改存储在堆上的值,从而更改实际的参数值

显然,我遗漏了一些东西,因为我必须返回一个字符串并手动将其分配给属性。如果有人能指出我的误解,我将不胜感激


谢谢。

我认为这里缺少的一点是:字符串是不可变的

尽管您通过引用传递它,但只要有任何东西试图改变字符串,就会创建一个新字符串,而旧字符串保持不变

我相信它是唯一一个强制实现不变性的引用类型

发件人:

字符串是不可变的——不能更改字符串对象的内容 创建对象后更改,尽管语法使其 看起来好像你能做到这一点。例如,当您编写此代码时, 编译器实际上创建了一个新字符串对象来保存新字符串 字符序列,并且该新对象被指定给b。这个 然后字符串“h”有资格进行垃圾收集

进一步阅读:

如果您希望该方法“更改”调用方的字符串,则可以使用
ref
关键字模拟:

public void SelectFile(ref string propertyName)
{
    propertyName = browserWindow.FileName;  
}
在本例中,将在方法中为参数
propertyName
赋值,因为使用了
ref
,这也会更改调用者指向的字符串。请注意,这里仍然强制执行不变性
propertyName
用于指向字符串A,但赋值后现在指向字符串B-旧字符串A现在未被引用并将被垃圾收集(但重要的是仍然存在且未更改-不可变)。如果未使用
ref
关键字,调用者仍将指向A,方法将指向B。但是,由于使用了
ref
关键字,调用者变量现在将指向字符串B

这与以下示例的效果相同:

static void Main(string[] args)
{
    MyClass classRef = new MyClass("A");
    PointToANewClass(ref classRef);
    // classRef now points to a brand new instance containing "B".
}

public static void PointToANewClass(ref MyClass classRef)
{
    classRef = new MyClass("B");
}
如果在不使用
ref
关键字的情况下尝试上述
classRef
仍将指向包含“A”的对象,即使该类是通过引用传递的


不要混淆字符串语义和
ref
语义。也不要混淆通过引用传递和赋值传递。从技术上讲,Stuff从不通过引用传递,堆上对象的指针通过值传递,因此引用类型上的
ref
具有上面指定的行为。同样,不使用
ref
也不允许在调用方和方法之间“共享”新的赋值,方法已收到指向堆上对象的指针的自身副本,取消引用指针具有通常的效果(查看相同的基础对象),但是分配给指针不会影响指针的调用方副本。

字符串是不可变的,因此您将它们的副本传递给方法。这意味着副本会更改,但原始参数保持不变。

我非常感谢,因为我终于了解了.NET framework如何使用引用参数以及字符串会发生什么

在.NET中,有两种数据类型:

  • 值类型:基本类型,如int、float、bool等
  • 引用类型:所有其他对象,包括字符串
对于引用类型,对象存储在堆中,变量仅保存指向该对象的引用。您可以通过引用访问对象的属性并对其进行修改。当您将其中一个变量作为参数传递时,指向同一对象的引用的副本将传递给方法体。因此,当您访问和修改属性时,您正在修改堆上存储的同一对象。即,该类是一个参考对象:

    public class ClassOne
    {
        public string Desc { get; set; }
    }
当你这么做的时候

    ClassOne one = new { Desc = "I'm a class one!" };
堆上有一个对象被引用
one
指向。如果您这样做:

    one.Desc = "Changed value!";
    public void ChangeOne(ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }
  string s = "HELL";
  s = s + "O";
堆上的对象已被修改。如果将此引用作为参数传递:

    public void ChangeOne(ClassOne one)
    {
        one.Desc = "Changed value!"
    }
堆上的原始对象也发生了更改,因为
one
保存了指向堆上相同对象的原始引用的副本

但如果你这样做:

    one.Desc = "Changed value!";
    public void ChangeOne(ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }
  string s = "HELL";
  s = s + "O";
原始对象不变。这是因为
one
是引用的副本,它现在指向另一个对象

如果通过引用显式传递它:

    public void ChangeOne(ref ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }
one
此方法内部不是外部引用的副本,而是引用本身,因此,原始引用现在指向此新对象

字符串是不可变的。这意味着您不能更改字符串。如果您尝试这样做,将创建一个新字符串。因此,如果您这样做:

    one.Desc = "Changed value!";
    public void ChangeOne(ClassOne one)
    {
        one = new ClassOne { Desc ="Changed value!" };
    }
  string s = "HELL";
  s = s + "O";
第二行创建了一个新的string实例,其值为“HELLO”且“HELL”被丢弃在堆上(留下进行垃圾收集)

因此,如果将其作为如下参数传递,则无法更改:

    public void AppendO(string one)
    {
        one = one + "O";
    }

    string original =  "HELL";
    AppendO(original);
原始的
字符串保持原样。函数中的代码创建一个新对象,并将其指定给一个对象,该对象是原始引用的副本。但原文一直指向“地狱”

对于值类型,当它们作为参数传递给函数时,它们通过值传递,即函数