代码是如何通过引用传递的?

时间:2009-10-23 17:39:54

标签: c# delegates

在main中我声明了一个本地int []数组(int [] nums)。我没有通过引用传递它。  但是当我打印局部数组的值时,我得到每个元素的平方值。  这是什么原因?

delegate void tsquare(int[] a);

static void Main()
{
     int[] nums = { 1, 2, 3 };
     tsquare sqr = new tsquare(SomeClass.Square);
     sqr(nums);

     foreach (int intvals in nums)
     {
       Console.WriteLine(intvals);
     }
}


   class SomeClass
   {

     public static void Square(int[] array)
     {
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

   }

更新

我对所有人的批评。我认为是int [] {Array}是一个值类型,而委托完成了 关于它的一些技巧。现在从你的回答中,我理解Array是引用类型。

7 个答案:

答案 0 :(得分:3)

这里有两个概念。

  1. 参考类型与价值类型
  2. 传递价值与传递参考
  3. 让我们先解决第二个问题。

    按值传递内容意味着您为该方法提供了该值的副本,并且可以随意更改该值,而不会将这些更改泄漏回调用该方法的代码中。

    例如,这个:

    Int32 x = 10;
    SomeMethod(x); // pass by value
    

    在这种情况下,在调用返回之后,x将不会是10以外的任何东西,因为无论SomeMethod对其值的副本做了什么,它只会做到它自己的值。

    然而,通过引用传递意味着我们并没有真正给出该方法自己的值,而是我们给它在我们自己的值所在的内存中的位置,因此方法对该值的任何作用将是反映回我们的代码,因为实际上,只有一个价值在起作用。

    所以这个:

    Int32 x = 10;
    SomeMethod(ref x); // pass by reference
    

    在这种情况下,x可能在SomeMethod返回后保持的值不同于调用之前的值。

    所以这是通过价值而不是通过参考传递。

    现在要混淆水域。还有另一个概念,参考类型与价值类型,许多人混淆。你的问题暗示你也对这个问题感到困惑,如果你不这样做,我表示道歉。

    参考类型实际上是一个由两部分组成的东西。它是一个参考,它是参考引用的任何内容。想想你知道地址的房子。你在一张纸上写下这个地址实际上并没有将整个房子放在那张纸上,而是你对你纸上的那个房子有一个“参考”。

    .NET中的引用类型是一回事。在内存中的某处,有一个对象,它是一组值,组合在一起。存储在变量中的此对象的地址。这个变量被声明为一个引用类型的类型,它允许这个由两部分组成的交易。

    引用类型的好处是你可能有很多对同一个实际对象的引用,所以即使你复制引用,你仍然只有一个对象在内存中。

    编辑:关于问题,数组是引用类型。这意味着您的变量只保存实际数组的地址,并且该数组对象位于内存中的其他位置。

    值类型是一回事,整个值是“值类型”的一部分,当你复制它时,你会制作不同的副本

    以下是值类型的示例:

    struct SomeType
    {
        public Int32 Value;
    }
    
    SomeType x = new SomeType;
    x.Value = 10;
    SomeType y = x; // value type, so y is now a copy of x
    y.Value = 20; // x.Value is still 10
    

    但是,对于引用类型,您不是要复制它引用的对象,而是只复制引用。可以把它想象成将那个房子的地址复制到第二张纸上。你仍然只有一所房子。

    因此,只需将SomeType的类型更改为引用类型(将struct更改为class):

    class SomeType
    {
        public Int32 Value;
    }
    
    SomeType x = new SomeType;
    x.Value = 10;
    SomeType y = x; // reference type, so y now refers to the same object x refers to
    y.Value = 20; // now x.Value is also 20, since x and y refer to the same object
    

    现在最后的事情;按值传递引用类型。

    采取这种方法:

    public void Test(SomeType t)
    {
        t.Value = 25;
    }
    

    鉴于我们上面的SomeType的类版本,我们这里有一个采用引用类型参数的方法,但它将它作为值传递。

    这意味着Test无法更改t以完全引用另一个对象,并使该更改泄漏回调用代码。把这想象成打电话给朋友,给他你在纸上的地址。无论你的朋友在那所房子里做什么,你在纸上的地址都不会改变。

    但是,该方法可以自由修改对象的内容引用。在那个房子/朋友场景中,你的朋友可以自由地去那个房子,并重新安排家具。因为只有一个房子在玩,如果你重新安排它后去那个房子,你会看到他的变化。

    如果更改方法以通过引用传递引用类型,则不仅该方法可以自由重新排列所引用对象的内容,而且该方法也可以使用一个完全新的对象替换该对象,并且将更改反映回调用代码。基本上,你的朋友可以告诉你“从现在开始,使用这个我会读给你的新地址而不是旧地址,完全忘掉旧地址。”

答案 1 :(得分:2)

在C#中,默认情况下所有参数都按值传递。 C#中有两种类型,即值和引用类型。

作为参数传递给函数时的引用类型变量仍将按值传递;也就是说,如果函数更改了该变量引用的对象,则在函数完成后,传入的变量仍将引用与在同一上下文中调用函数之前相同的对象(包括null)。

但是,如果在声明函数参数时使用ref修饰符,则函数可能会更改调用者上下文中变量引用的对象。

对于值类型,这更直接,但它是相同的概念。请记住,int[]是引用类型(与所有数组一样)。

在传递一些int数组时考虑这些函数的不同之处:

     public static void Square1(int[] array)
     {
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

     public static void Square2(int[] array)
     {
         array = {10, 20, 30};
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

     public static void Square3(ref int[] array)
     {
         array = {10, 20, 30};
         for (int i = 0; i < array.Length; i++)
         {
             array[i] = array[i] * array[i];
         }
     }

答案 2 :(得分:2)

数组引用由值自动传递,因为它是引用类型

读:

Reference Types

Value Types

答案 3 :(得分:2)

其他大多数答案都是正确的,但我认为术语令人困惑并需要解释。默认情况下,您可以说C#中的所有参数都是按值传递的,这意味着变量的内容将复制到方法变量中。这对于值类型的变量是直观的,但诀窍在于记住作为引用类型(包括数组)的变量实际上是指针。传入时,指针包含的内存位置将复制到该方法中。

应用 ref 修饰符时,该方法从调用者获取实际变量。在大多数情况下,行为是相同的,但请考虑以下内容:

public void DoesNothing(int[] nums)
{
  nums = new []{1, 2, 3, 4};
}

在DoesNothing中,我们实例化一个新的int数组并将其分配给nums。当方法退出时,调用者看不到赋值,因为该方法正在操作传入的引用(指针)的副本。

public void DoesSomething(ref int[] nums)
{
  nums = new []{1, 2, 3, 4};
}

使用 ref 关键字,该方法基本上可以从调用者那里伸出并影响原始变量本身。

要实现您最初想要的内容,您可以创建一个新数组并将其返回,或者在调用者中使用Array.CopyTo()。

答案 4 :(得分:1)

你没有通过引用传递它。数组是按值传递的,但.NET中的数组是引用类型,因此您传入对数组的引用,这就是您看到值的平方值的原因。

答案 5 :(得分:0)

数组是对象,通过引用传递。 Ints是结构并按值传递(除非您在方法签名中使用ref关键字,根据评论中的挑剔的人)(谁是正确的)(但挑剔)。

答案 6 :(得分:0)

阅读以下SO问题 - 它解释了传值和传递参考之间的差异。接受的答案中有一个关于该主题的好文章的链接,可以帮助您理解差异。

what is different between Passing by value and Passing by reference using C#