我正在学习(新手).NET,我有些疑惑。
从书中读到的例子我了解到String是对象,然后是Reference Type。
所以,我做了这个测试,结果与我的预期不同:
我真的很好奇,这是一个例外,因为“字符串”是特殊类型吗?
class Program
{
static void Main(string[] args)
{
SByte a = 0;
Byte b = 0;
Int16 c = 0;
Int32 d = 0;
Int64 e = 0;
string s = "";
Exception ex = new Exception();
object[] types = { a, b, c, d, e, s, ex };
// C#
foreach (object o in types)
{
string type;
if (o.GetType().IsValueType)
type = "Value type";
else
type = "Reference Type";
Console.WriteLine("{0}: {1}", o.GetType(), type);
}
// Test if change
string str = "I'll never will change!";
Program.changeMe(str);
Console.WriteLine(str);
}
public static string changeMe(string param)
{
param = "I have changed you!!!";
return ""; // no return for test
}
}
输出:
System.SByte: Value type
System.Byte: Value type
System.Int16: Value type
System.Int32: Value type
System.Int64: Value type
System.String: Reference Type
System.Exception: Reference Type
I'll never will change!
答案 0 :(得分:19)
String确实是一种引用类型。但是,当您的Main方法调用changeMe(str)时,.NET会在param参数中将str的引用副本传递给changeMe。然后,changeMe修改此副本以引用“我已经改变了你!!!”,但原始的str引用仍然指向“我永远不会改变”。
作为引用类型意味着如果您更改了传递的字符串的状态,则调用者将看到这些更改。 (您不能对字符串执行此操作,因为字符串是不可变的,但您可以将其用于其他引用类型,例如Control。)但重新分配参数不会更改传入的调用方的值参数,即使该值是参考。
答案 1 :(得分:13)
字符串是引用。
changeMe
不会更改字符串,只是在该函数中重新分配本地引用(指针)。
现在,如果您将字符串作为ref
arg传递,您可以获得更多乐趣:
public static string changeMe(ref string param) {
param = "I have changed you!!!";
return ""; // no return for test
}
答案 2 :(得分:4)
这与字符串是值类型或引用类型无关。您的changeMe方法的参数未标记为ref
,因此如果方法更改它,则调用者将看不到更改。
请尝试改为:
public static string changeMe(ref string param)
{
param = "I have changed you!!!";
return ""; // no return for test
}
答案 3 :(得分:4)
Program.changeMe(str)不会导致str设置为“我改变了你!!!”的原因是虽然string是引用类型,但引用是按值传递的。
所以param是引用的副本,它在Program.changeMe(param)的范围内被更改,然后在方法结束时被抛弃。
参考str仅被复制,而不是更改为引用“我已经改变了你!!!”
答案 4 :(得分:2)
字符串确实是一种特殊情况。它们是引用类型,但行为类似于值类型。这是为.Net开发团队的“简单性”而做的。
好极了: 它是一种参考类型 字符串是值类型是一种常见的误解。那是因为它的不变性(见下一点)使它有点像一种价值类型。它实际上就像一个普通的引用类型。有关值类型和引用类型之间差异的更多详细信息,请参阅有关参数传递和内存的文章。
答案 5 :(得分:1)
MSDN page可以帮助解释它为什么是参考类型。
因为String
是char
的集合并且继承自各种Collection基类,所以它是一个类,因此是一个引用类型。
答案 6 :(得分:1)
是的,这看起来很奇怪。发生的事情是您在参数中传递了对字符串的引用,但是您将其传递给按值,即您在参数中创建了引用的副本。因此,当您在函数范围内重新分配它时,您只需要更改changeMe()方法的字符串引用的本地副本。
有关这方面的更多信息: http://www.yoda.arachsys.com/csharp/parameters.html
答案 7 :(得分:0)
您按值传递并制作了引用的副本。通过引用传递它将改变。好吧,真的会创建一个新的字符串,你的参考将是一个新的位置。
答案 8 :(得分:0)
如果您打算将参数的值更改为方法,则应该使用“ref”或“out”关键字。否则,您可能会制造令人困惑的方法,这些方法可能会也可能不会达到您的意图。正如其他人指出的那样,你正在改变字符串引用的值而不是对象的实例。要查看其如何与其他类一起使用,我已经编写了一个快速示例
static void Main(string[] args)
{
var str = "String";
var obj = new MyObject() { Value = "Object" };
Console.WriteLine(str); //String
Console.WriteLine(obj); //Object
ChangeMe(str);
ChangeMe(obj);
Console.WriteLine(str); //String
Console.WriteLine(obj); //Object
ChangeMeInner(obj);
// this is where it can get confusing!!!
Console.WriteLine(obj); //Inner
ChangeMe(ref str);
ChangeMe(ref obj);
Console.WriteLine(str); // Changed
Console.WriteLine(obj); // Changed
Console.Read();
}
class MyObject
{
public string Value { get; set; }
public override string ToString()
{
return Value;
}
}
static void ChangeMe(MyObject input)
{
input = new MyObject() { Value = "Changed" };
}
static void ChangeMeInner(MyObject input)
{
input.Value = "Inner";
}
static void ChangeMe(string input)
{
input = "Changed";
}
static void ChangeMe(ref MyObject input)
{
input = new MyObject() { Value = "Changed" };
}
static void ChangeMe(ref string input)
{
input = "Changed";
}
答案 9 :(得分:-1)
对于所有其他引用类型,使用byval语义(没有ref或out)传递它意味着您无法更改调用者复制指向的堆上的哪个对象,这并不意味着您可以' t更改变量指向的堆上的对象的内容。因为字符串是不可变的,所以它们确实是一个例外,它们在这方面的行为就像值类型一样,即使它们是引用类型,但这是通过CLR中的特殊附加编码来完成的。