我知道C#中的"string"是一个引用类型.这是在MSDN上.但是,此代码不能正常工作:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
Run Code Online (Sandbox Code Playgroud)
输出应该在"传递之前""传递之后",因为我将字符串作为参数传递并且它是引用类型,第二个输出语句应该识别在TestI方法中更改了文本.但是,我在"通过之前""在通过之前"得到它似乎是通过值传递而不是通过ref.我知道字符串是不可变的,但我不知道这将如何解释这里发生的事情.我错过了什么?谢谢.
Jon*_*eet 204
对字符串的引用按值传递.通过值传递引用和通过引用传递对象之间存在很大差异.不幸的是,在两种情况下都使用了"引用"这个词.
如果你这样做把这个字符串参考的参考,因为你希望它会工作:
using System;
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(ref test);
Console.WriteLine(test);
}
public static void TestI(ref string test)
{
test = "after passing";
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您需要区分对引用所引用的对象进行更改,以及对变量(例如参数)进行更改以使其引用其他对象.我们无法对字符串进行更改,因为字符串是不可变的,但我们可以使用以下代码StringBuilder来演示它:
using System;
using System.Text;
class Test
{
public static void Main()
{
StringBuilder test = new StringBuilder();
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(StringBuilder test)
{
// Note that we're not changing the value
// of the "test" parameter - we're changing
// the data in the object it's referring to
test.Append("changing");
}
}
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅有关参数传递的文章.
Mar*_*rov 32
如果我们必须回答这个问题:String是一个引用类型,它的行为就像一个引用.我们传递一个包含引用的参数,而不是实际的字符串.问题在于功能:
public static void TestI(string test)
{
test = "after passing";
}Run Code Online (Sandbox Code Playgroud)
该参数test保存对字符串的引用,但它是一个副本.我们有两个指向字符串的变量.因为任何带字符串的操作实际上都会创建一个新对象,所以我们将本地副本指向新字符串.但原始test变量没有改变.
建议的解决方案放在ref函数声明和调用工作中,因为我们不会传递test变量的值,而只会传递对它的引用.因此,函数内部的任何更改都将反映原始变量.
我想在最后重复:String是一个引用类型,但由于它是不可变的,所以该行test = "after passing";实际上创建了一个新对象,并且该变量的副本test被更改为指向新字符串.
Der*_*k W 24
正如其他人所说,String.NET中的类型是不可变的,它的引用是按值传递的.
在原始代码中,只要此行执行:
test = "after passing";
Run Code Online (Sandbox Code Playgroud)
然后test不再是指原始对象.我们创建了一个新 String对象,并指定test在托管堆上引用该对象.
我觉得很多人都被绊倒了,因为没有可见的正式构造提醒他们.在这种情况下,它发生在幕后,因为该String类型在构造方式方面具有语言支持.
因此,这就是为什么在方法test范围之外看不到更改的原因TestI(string)- 我们已经通过值传递了引用,现在该值已经改变了!但是如果String引用是通过引用传递的,那么当引用更改时,我们将看到它超出了TestI(string)方法的范围.
无论是REF或出需要在这种情况下关键字.我觉得out关键字可能稍微适合这种特殊情况.
class Program
{
static void Main(string[] args)
{
string test = "before passing";
Console.WriteLine(test);
TestI(out test);
Console.WriteLine(test);
Console.ReadLine();
}
public static void TestI(out string test)
{
test = "after passing";
}
}
Run Code Online (Sandbox Code Playgroud)
Mes*_*ssi 18
“一张照片胜过千言万语”。
我这里有一个简单的例子,它类似于你的情况。
string s1 = "abc";
string s2 = s1;
s1 = "def";
Console.WriteLine(s2);
// Output: abc
Run Code Online (Sandbox Code Playgroud)
这是发生的事情:
s1和s2变量引用同一个"abc"字符串对象。"abc"字符串对象不会修改自己(到"def"),而是"def"创建一个新的字符串对象,然后s1引用它。s2仍然引用"abc"字符串对象,这就是输出。实际上对于任何对象来说都是相同的,即作为引用类型并且通过引用传递是c#中的两个不同的东西.
这可行,但无论类型如何都适用:
public static void TestI(ref string test)
Run Code Online (Sandbox Code Playgroud)
关于字符串作为引用类型,它也是一个特殊的类型.它的设计是不可变的,因此它的所有方法都不会修改实例(它们返回一个新实例).它还有一些额外的东西用于提高性能.
这是考虑值类型,传值,引用类型和传递引用之间差异的好方法:
变量是一个容器.
值类型变量包含实例.reference-type变量包含指向存储在别处的实例的指针.
修改value-type变量会改变它包含的实例.修改引用类型变量会改变它指向的实例.
单独的引用类型变量可以指向同一个实例.因此,可以通过指向它的任何变量来突变相同的实例.
传值参数是一个带有新内容副本的新容器.传递引用参数是具有原始内容的原始容器.
当value-type参数按值传递时:重新分配参数的内容对范围外没有影响,因为容器是唯一的.修改参数对范围外没有影响,因为实例是一个独立的副本.
当reference-type参数按值传递时:重新分配参数的内容对范围外没有影响,因为容器是唯一的.修改参数的内容会影响外部作用域,因为复制的指针指向共享实例.
当任何参数通过引用传递时:重新分配参数的内容会影响外部范围,因为容器是共享的.修改参数的内容会影响外部范围,因为内容是共享的.
结论:
字符串变量是引用类型变量.因此,它包含指向存储在别处的实例的指针.当按值传递时,会复制其指针,因此修改字符串参数应该会影响共享实例.但是,字符串实例没有可变属性,因此无论如何都不能修改字符串参数.当通过引用传递时,指针的容器是共享的,因此重新分配仍将影响外部范围.
为了好奇并完成对话: 是的,String 是一种引用类型:
unsafe
{
string a = "Test";
string b = a;
fixed (char* p = a)
{
p[0] = 'B';
}
Console.WriteLine(a); // output: "Best"
Console.WriteLine(b); // output: "Best"
}
Run Code Online (Sandbox Code Playgroud)
但请注意,此更改仅适用于不安全的块!因为字符串是不可变的 (来自 MSDN):
字符串对象的内容在创建对象后就无法更改,尽管语法使您看起来好像可以执行此操作。例如,当您编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,并将该新对象分配给 b。然后字符串“h”就可以进行垃圾回收。
string b = "h";
b += "ello";
Run Code Online (Sandbox Code Playgroud)
请记住:
尽管字符串是引用类型,但定义相等运算符 (
==和!=) 是为了比较字符串对象的值,而不是引用。
上面的答案很有帮助,我只想添加一个示例,我认为它可以清楚地说明当我们传递没有 ref 关键字的参数时会发生什么,即使该参数是引用类型:
MyClass c = new MyClass(); c.MyProperty = "foo";
CNull(c); // only a copy of the reference is sent
Console.WriteLine(c.MyProperty); // still foo, we only made the copy null
CPropertyChange(c);
Console.WriteLine(c.MyProperty); // bar
private void CNull(MyClass c2)
{
c2 = null;
}
private void CPropertyChange(MyClass c2)
{
c2.MyProperty = "bar"; // c2 is a copy, but it refers to the same object that c does (on heap) and modified property would appear on c.MyProperty as well.
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
116360 次 |
| 最近记录: |