当作为方法参数传递时,如何处理值和引用类型?

时间:2014-10-14 14:57:48

标签: c# methods parameters reference

我创建了一个简单的控制台应用程序来演示我的问题。

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;
    }
}

更改所有三个变量的值并重新分配后,bstr2的值仍然与astr相同,但{{ 1}}由两行组成,我没想到。我认为dt2的值只包含1行,因为我没有从dt2方法返回任何内容。

有人可以开导一下吗?

2 个答案:

答案 0 :(得分:3)

这里有一些概念:

  1. 传递给函数的值类型不会改变,这就是为什么a的值永远不会改变,你只传递a的值而不是实际的参考值到a所在的记忆位置。

  2. strdt是引用类型,在正常情况下会更改其值,因为您要传递内存位置和实际对象本身才能更改,而不仅仅是该值的副本,您可以在评论中查看链接以获取更多相关信息。

  3. 但是,str即使它是引用类型也没有改变,而且因为你碰到了另一个.NET规则,字符串是不可变的,不能改变。当我说.NET中的字符串不可变时,这意味着你无法更改它们,请参阅此处获取更多信息:MSDN - string (C# reference)

  4. 正如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();
    }
  5. 已修改另一方面,虽然不是您在代码示例中演示的概念的一部分,但正如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参数,您实际上并未将参数重新分配给新值(就像使用intstring值一样),你只是修改了一个班级成员。

From MSDN

  

当您按值传递reference-type参数时,可以更改引用所指向的数据,例如类成员的值。

此更改在方法之外可见,因为两个变量(ChangeValueMain内部)都引用相同的参考。

我建议在Parameter passing in C#阅读Jon Skeet的文章,以便全面了解正在发生的事情。