繁体   English   中英

通过引用传递引用vs通过值传递引用-C#

[英]Pass reference by reference vs pass reference by value - C#

问候,

我得到按值传递和按引用传递之间的区别。 但是我似乎无法理解通过ref传递引用(例如array)和通过value传递数组。 如何通过参考传递参考?

     int[] myArray = {1,2,3};
     PassByVal(myArray);
     PassByRef(ref myArray);

     PassByVal(int[] array)
     {    array = new int[] {7,8,9};   // will not work }

     PassByRef(ref int[] array)
     {    array = new int[] {10,11,12}; }  // will work

如果逐个引用地传递引用,则可以使变量直接指向新对象。 如果按值传递引用,您仍然可以更改对象的状态,但不能使变量指向其他对象。

例:

void RefByRef(ref object x)
{
  x=new object(2);
}

void RefByValue(object x)
{
 x=new object(2);//Only changes a local variable and gets discarded once the function exits
}

void Test()
{
  object x1=1;
  object x1a=x1;
  RefByRef(ref x1);
  //x1 is now a boxed 2
  //x1a is still a boxed 1


  object x2=1;
  RefByValue(x2);
  //x2 is still a boxed 1
}

为了回答您的问题,让我们首先看一下ValueTypes。ValueType包含Value。 就是说,它并不指向另一个保存该值的存储位置,而是它的存储位置就是该值。

所以int = 10;

int j = i;

这里发生的是将i的值的副本分配给j。 它们都具有相同的值,但是它们在内存中的位置不同。 换句话说,每次将一个值类型分配给另一个值类型时,都会创建一个副本。

与ReferenceTypes签订合同。

o = 10;

对象p = o;

因为o是ReferenceType,所以o指向保存值为10的内存位置(它实际上是装箱的,但我将使其保持简单)。 现在,在下一行中,p指向相同的存储位置。 换句话说,参考轮胎有两件事。 1.地址指针。2.该地址指向的实际存储器位置,用于保存实际的“事物”。

如果您能做到这一点,那么我们可以继续按值和按引用进行传递。

在C#中,参数按值传递。 因此,如果将valueType传递给需要valuetype参数的方法,则

int i = 10;
SomeMethod(i);
Console.WriteLine(i);

static void SomeMethod(int value)
{
  value = 20;
}

当调用SomeMethod时,会将i值的副本发送到该方法。 如果该方法操纵参数,则不会影响原始变量i。 因此,您将在控制台窗口中看到10。

与参考类型签约;

  class Program
  {
    static void Main(string[] args)
    {
      Customer c = new Customer() { Name = "Mike" };
      SomeMethod(c);
      Console.WriteLine(c.Name);
    }

    static void SomeMethod(Customer customer)
    {
      customer.Name = "John";
    }
  }

  class Customer
  {
    public string Name { get; set; }
  }

由于c是引用类型。 C#通过值传递参数。 传递引用的“值”的副本。 那就是地址c指向的值被传递。 在该方法中,由于地址相同(它是一个副本但指向相同的内存位置),因此该方法能够操纵对象的状态。 因此,您将在控制台窗口中看到的是“ John”而不是“ Mike”。

但是,如果该方法尝试将另一个实例分配给该参数(在这种情况下称为“客户”)。 然后事情变了。

  class Program
  {
    static void Main(string[] args)
    {
      Customer c = new Customer() { Name = "Mike" };
      SomeMethod(c);
      Console.WriteLine(c.Name);
    }

    static void SomeMethod(Customer customer)
    {
      customer = new Customer();
      customer.Name = "John";
    }
  }

  class Customer
  {
    public string Name { get; set; }
  }

注意,在该方法中,我们创建了一个Customer的新实例,并将其分配给参数customer,并将此新实例的名称设置为“ John”。 我们将在控制台窗口中看到的是“麦克”,而不是约翰。

这是因为在将原始变量(c)传递给该方法之前已对其进行了复制。 现在在该方法中,我们有另一个地址,然后操纵该新地址,以使原始实例保持不变。 说得通?

好吧,如果有道理。 那么,如果我们实际上希望SomeMethod能够完成我们尝试做的事情呢? 好吧,那么该参数不能按值传递,而必须按引用传递。 意味着正在传递变量c和两个部分(它指向的地址值和地址本身)。 因此,现在您通过引用传递引用类型。

  class Program
  {
    static void Main(string[] args)
    {
      Customer c = new Customer() { Name = "Mike" };
      SomeMethod(ref c);
      Console.WriteLine(c.Name);
    }

    static void SomeMethod(ref Customer customer)
    {
      customer = new Customer();
      customer.Name = "John";
    }
  }

  class Customer
  {
    public string Name { get; set; }
  }

我建议您检查一下此链接 它非常有用,并且包含有关C#中参数传递的非常简单的示例。

引用参数不会传递函数成员调用中使用的变量的值-它们本身使用变量。 不会在函数成员声明中为变量创建新的存储位置,而是使用相同的存储位置 ,因此函数成员中的变量值和引用参数的值将始终相同。 参考参数需要ref修饰符作为声明和调用的一部分-这意味着当您通过引用传递某些内容时,它始终很清晰。 让我们看一下前面的示例,只是将参数更改为参考参数:

void Foo (ref StringBuilder x) {
    x = null;
}

...

StringBuilder y = new StringBuilder();
y.Append ("hello");
Foo (ref y);
Console.WriteLine (y==null); // will write TRUE

在你的例子中

int[] myArray = {1,2,3};
PassByVal(myArray);
PassByRef(ref myArray);

PassByVal(int[] array){
    // the function copy the value of the pointer in a new location of memory
    // the "copied" pointer still points to the array 123    

    // now you are modifying the object pointed by THE COPY of the pointer
    // the original pointer still points to array 123
    // the copy of the pointer will point to array 456
    array = new int[] {7,8,9}; 

} // will not work

PassByRef(ref int[] array){
   // here you are passing the pointer without creating a copy of it in a 
   // new location of memory

   // we have not a original pointer and a "copyed" pointer
   // we have only the original pointer and now whe point it to array 10,11,12
   array = new int[] {10,11,12}; 
}  // will work

这似乎有些令人困惑,但实际上并不是那么棘手。 当您将引用类型的实例分配给变量时,可以说该变量的将是对对象的引用 ,而不是对象本身。 当您将该变量按值传递给另一个方法时,您正在传递引用的副本。 被调用方法将“看到”与调用代码相同的实例。 如果改为通过引用传递变量,则调用方法将获得与调用代码相同的引用副本

它们之间的行为差​​异是,当您通过引用传递变量时,被调用的方法可能会为该变量分配另一个引用(使其引用另一个相同类型的实例),并且调用代码将看到此更改。 除非您进行此类分配,否则无需使用ref

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM