我创建了一个简单的控制台应用程序来演示我的问题。
class Program
{
static void Main(string[] args)
{
var a = 2;
var str = "John";
var dt = GetDataTable();
ChangeValue(a, str, dt);
var b = a; // **still 2**
var str2 = str; // **still John**
var dt2 = dt; // **changed and consists of 2 rows, why??????????????**
}
private static void ChangeValue(int a, string str, DataTable dt)
{
a += 1;
str = "Hello " + str;
dt.Rows.Add(2, "Smith", "London");
}
private static DataTable GetDataTable()
{
var dt = new DataTable();
dt.Columns.Add("Id");
dt.Columns.Add("Name");
dt.Columns.Add("City");
dt.Rows.Add(1, "John", "London");
return dt;
}
}
更改所有三个变量的值并重新分配后,b
和str2
的值仍然与a
和str
相同,但{{ 1}}由两行组成,我没想到。我认为dt2
的值只包含1行,因为我没有从dt2
方法返回任何内容。
有人可以开导一下吗?
答案 0 :(得分:3)
这里有一些概念:
传递给函数的值类型不会改变,这就是为什么a
的值永远不会改变,你只传递a
的值而不是实际的参考值到a
所在的记忆位置。
str
和dt
是引用类型,在正常情况下会更改其值,因为您要传递内存位置和实际对象本身才能更改,而不仅仅是该值的副本,您可以在评论中查看链接以获取更多相关信息。
但是,str
即使它是引用类型也没有改变,而且因为你碰到了另一个.NET规则,字符串是不可变的,不能改变。当我说.NET中的字符串不可变时,这意味着你无法更改它们,请参阅此处获取更多信息:MSDN - string (C# reference)。
正如Andrew所指出的那样,只传回对引用对象的属性或字段的更改。重新分配,不是;例如,如果您已将dt
方法中ChangeValue
的分配更改为new DataTable()
,则代码仍会显示原始数据表。
private static void ChangeValue(ref int a, string str, DataTable dt)
{
a += 1;
str = "Hello " + str;
dt.Rows.Add(2, "Smith", "London");
dt = new DataTable();
}
已修改另一方面,虽然不是您在代码示例中演示的概念的一部分,但正如Jeroen指出的那样,有一个关键字ref
可以更改值类型的方式被传递给函数。
例如,如果您更改了:
private static void ChangeValue(int a, string str, DataTable dt)
{
a += 1;
str = "Hello " + str;
dt.Rows.Add(2, "Smith", "London");
}
到
private static void ChangeValue(ref int a, string str, DataTable dt)
{
a += 1;
str = "Hello " + str;
dt.Rows.Add(2, "Smith", "London");
}
然后改变了
ChangeValue(a, str, dt);
到
ChangeValue(ref a, str, dt);
a
的价值现在会改变。通过在函数声明和调用它时包含ref关键字,您不再只是传递值,而是实际引用存储该值的对象/内存位置。因此,当它的功能发生变化时,新值应为3。
还有out
关键字,可以以相同的方式使用,技术上也可以做同样的事情。我所知道的唯一区别是,使用out
关键字传递的变量不必先进行初始化,而使用ref
关键字传递的变量可以。还有一个语义差异(意义上的差异),因为out
变量应该是函数控制的变量,它们是从它传递的;虽然ref
变量在用户/程序员控制中,但该函数恰好需要引用,并且可能会改变状态。
另外,我添加了dotnetfiddle以更详细地展示示例。
答案 1 :(得分:2)
这与ChangeValue
的返回类型没有任何关系,它与参数在C#中的传递方式有关。
默认情况下,参数按值传递。
从您的问题看,您似乎理解第一点。
如果修改DataTable
参数,您实际上并未将参数重新分配给新值(就像使用int
和string
值一样),你只是修改了一个班级成员。
当您按值传递reference-type参数时,可以更改引用所指向的数据,例如类成员的值。
此更改在方法之外可见,因为两个变量(ChangeValue
和Main
内部)都引用相同的参考。
我建议在Parameter passing in C#阅读Jon Skeet的文章,以便全面了解正在发生的事情。