C# 4.0 字符串类型的属性的传递方式
我有以下代码(注意下面的代码不会更新属性) 及 现在我意识到我需要更改第二个方法,以便它返回一个字符串并手动将其分配给第一个示例中的属性 我不确定的是,当我将ref类型指定为方法的实际参数时,它在堆栈上的值的副本(即它在堆中的内存地址)被复制到堆栈上方法形式参数的新位置,因此它们都指向堆上的相同内存地址。因此,当我更改formal参数的值时,它实际上会更改存储在堆上的值,从而更改实际的参数值 显然,我遗漏了一些东西,因为我必须返回一个字符串并手动将其分配给属性。如果有人能指出我的误解,我将不胜感激C# 4.0 字符串类型的属性的传递方式,c#-4.0,properties,C# 4.0,Properties,我有以下代码(注意下面的代码不会更新属性) 及 现在我意识到我需要更改第二个方法,以便它返回一个字符串并手动将其分配给第一个示例中的属性 我不确定的是,当我将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);
原始的字符串保持原样。函数中的代码创建一个新对象,并将其指定给一个对象,该对象是原始引用的副本。但原文一直指向“地狱”
对于值类型,当它们作为参数传递给函数时,它们通过值传递,即函数