Передать ссылку по ссылке против передачи по значению - C # - PullRequest
3 голосов
/ 12 ноября 2010

Привет,

Я получаю разницу между передачей по значению и передачей по ссылке. Но передать ссылку (например, массив) по ссылке и передать массив по значению - это то, что я не могу понять. Как вы можете передать ссылку по ссылке?

     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

Ответы [ 4 ]

7 голосов
/ 12 ноября 2010

Если вы передаете ссылку по ссылке, вы можете сделать переменную, переданную в точку, новому объекту. Если вы передадите ссылку по значению, вы все равно сможете изменить состояние объекта, но не сможете сделать так, чтобы переменная указывала на другой объект.

Пример:

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
}
2 голосов
/ 12 ноября 2010

Чтобы ответить на ваш вопрос, давайте сначала посмотрим на ValueTypes ValueType содержит значение. То есть он, в свою очередь, не указывает на другую ячейку памяти, в которой хранится значение, а вместо этого его ячейка памяти является значением.

так int i = 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, передается. В методе, поскольку адрес один и тот же (это копия, но она указывает на то же место в памяти), метод может манипулировать состоянием объекта. Так что в окне консоли вы увидите «Джона», а не «Майка».

Однако, если метод пытается назначить другой экземпляр параметру (в данном случае это называется «клиент»). тогда все изменится.

  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; }
  }
1 голос
/ 12 ноября 2010

Предлагаю вам проверить эту ссылку .Это довольно полезно и содержит очень простые примеры о передаче параметров в 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 stil poin to the array 123    

    // now you are modifying the object pointed by THE COPY of the pointer
    // the original pointer stil point to array 123
    // the copoy 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
0 голосов
/ 12 ноября 2010

Это может показаться немного запутанным, но на самом деле это не так сложно.Когда вы назначаете экземпляр ссылочного типа переменной, вы можете сказать, что значение этой переменной будет ссылкой на объект, а не на сам объект.Когда вы передаете эту переменную по значению другому методу, вы передаете копию ссылки.Вызываемый метод «увидит» тот же экземпляр, что и вызывающий код.Если вместо этого вы передадите переменную по ссылке, вызывающий метод увидит такую ​​же копию ссылки , что и код вызова.Передав переменную по ссылке, вызываемый метод может назначить другую ссылку на переменную (сделать так, чтобы она ссылалась на другой экземпляр того же типа), и вызывающий код увидит это изменение.Если вы не делаете такие назначения, нет необходимости использовать ref.

...