Передача объектов по ссылке или значению в C # - PullRequest
204 голосов
/ 03 января 2012

В C # я всегда думал, что непримитивные переменные передаются по ссылке, а примитивные значения передаются по значению.

Таким образом, при передаче методу любого не примитивного объекта, все, что делается с объектом в методе, будет влиять на передаваемый объект. (Материал C # 101)

Однако я заметил, что когда я передаю объект System.Drawing.Image, что это не так? Если я передаю объект system.drawing.image другому методу и загружаю изображение в этот объект, то позволяю этому методу выйти из области видимости и вернуться к вызывающему методу, это изображение не загружается в исходный объект? *

Почему это?

Ответы [ 7 ]

430 голосов
/ 03 января 2012

Объекты вообще не передаются.По умолчанию аргумент оценивается, и его значение передается по значению в качестве начального значения параметра вызываемого вами метода.Теперь важным моментом является то, что значение является ссылкой для ссылочных типов - способ добраться до объекта (или ноль).Изменения в этом объекте будут видны из звонящего.Однако изменение значения параметра для ссылки на другой объект будет не видимым при использовании передачи по значению, которая используется по умолчанию для всех типов.

Если вы хотите использовать передачу по ссылке, вы должны использовать out или ref, независимо от того, является ли тип параметра типом значения или ссылочным типом.В этом случае, по сути, сама переменная передается по ссылке, поэтому параметр использует то же место хранения, что и аргумент, и вызывающий видит изменения в самом параметре.

Итак:

public void Foo(Image image)
{
    // This change won't be seen by the caller: it's changing the value
    // of the parameter.
    image = Image.FromStream(...);
}

public void Foo(ref Image image)
{
    // This change *will* be seen by the caller: it's changing the value
    // of the parameter, but we're using pass by reference
    image = Image.FromStream(...);
}

public void Foo(Image image)
{
    // This change *will* be seen by the caller: it's changing the data
    // within the object that the parameter value refers to.
    image.RotateFlip(...);
}

У меня есть статья , которая более подробно описана в этом .По сути, «передача по ссылке» не означает, что вы думаете, что это означает.

18 голосов
/ 07 февраля 2014

Еще один пример кода для демонстрации этого:

void Main()
{


    int k = 0;
    TestPlain(k);
    Console.WriteLine("TestPlain:" + k);

    TestRef(ref k);
    Console.WriteLine("TestRef:" + k);

    string t = "test";

    TestObjPlain(t);
    Console.WriteLine("TestObjPlain:" +t);

    TestObjRef(ref t);
    Console.WriteLine("TestObjRef:" + t);
}

public static void TestRef(ref int i)
{
    i = 5;
}

public  static void TestPlain(int i)
{
    i = 5;
}

public static void TestObjRef(ref string s)
{
    s = "TestObjRef";
}

public static void TestObjPlain(string s)
{
    s = "TestObjPlain";
}

И вывод:

TestPlain: 0

TestRef: 5

TestObjPlain: test

TestObjRef: TestObjRef

6 голосов
/ 16 сентября 2016

Я думаю, это понятнее, когда вы делаете это так.Я рекомендую загрузить LinqPad для проверки подобных вещей.

void Main()
{
    var Person = new Person(){FirstName = "Egli", LastName = "Becerra"};

    //Will update egli
    WontUpdate(Person);
    Console.WriteLine("WontUpdate");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateImplicitly(Person);
    Console.WriteLine("UpdateImplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");

    UpdateExplicitly(ref Person);
    Console.WriteLine("UpdateExplicitly");
    Console.WriteLine($"First name: {Person.FirstName}, Last name: {Person.LastName}\n");
}

//Class to test
public class Person{
    public string FirstName {get; set;}
    public string LastName {get; set;}

    public string printName(){
        return $"First name: {FirstName} Last name:{LastName}";
    }
}

public static void WontUpdate(Person p)
{
    //New instance does jack...
    var newP = new Person(){FirstName = p.FirstName, LastName = p.LastName};
    newP.FirstName = "Favio";
    newP.LastName = "Becerra";
}

public static void UpdateImplicitly(Person p)
{
    //Passing by reference implicitly
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

public static void UpdateExplicitly(ref Person p)
{
    //Again passing by reference explicitly (reduntant)
    p.FirstName = "Favio";
    p.LastName = "Becerra";
}

И это должно вывести

WontUpdate

Имя: Эгли, Фамилия: Бесерра

Обновление Явно

Имя: Фавио, Фамилия: Бесерра

Обновление явно

Имя: Фавио, Фамилия: Бесерра

3 голосов
/ 03 января 2012

Когда вы передаете объект типа System.Drawing.Image методу, вы фактически передаете копию ссылки на этот объект.

Так что, если внутри этого метода вы загружаете новое изображение, вы загружаете, используя новую / скопированную ссылку. Вы не вносите изменения в оригинал.

YourMethod(System.Drawing.Image image)
{
    //now this image is a new reference
    //if you load a new image 
    image = new Image()..
    //you are not changing the original reference you are just changing the copy of original reference
}
2 голосов
/ 03 января 2012

Как вы передали объект в метод?

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

Следующая ссылка поможет вам лучше понять.

http://dotnetstep.blogspot.com/2008/09/passing-reference-type-byval-or-byref.html

0 голосов
/ 26 декабря 2016

Принятый ответ звучит немного неправильно и сбивает с толку.Что такое «копия ссылки?»

Какое значение имеет следующее утверждение ?:

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

Его пример в своей ссылке пытается установить для экземпляра объекта значение NULL.Объект не был успешно установлен в нуль из-за автоматической сборки мусора.Его нельзя удалить таким образом.

Вот статья Microsoft, сравнивающая Java и C #.

С https://msdn.microsoft.com/en-us/library/ms836794.aspx

"Все объекты являются ссылками

Ссылочные типы очень похожи на указатели в C ++, особенно при установке идентификатора для некоторого нового экземпляра класса, но при доступе к свойствам или методам этого ссылочного типа используйте оператор ".", Который аналогичен доступу к экземплярам данных в C ++, которыйсоздаются в стеке. Все экземпляры классов создаются в куче с помощью оператора new, но удаление запрещено, поскольку оба языка используют свои собственные схемы сборки мусора, описанные ниже. "

0 голосов
/ 23 ноября 2015

In Pass By Reference. Вы только добавляете "ref" в параметры функции, и еще одна вещь, которую вы должны объявлять как функцию "static", потому что main является статической (# public void main(String[] args))!

namespace preparation
{
  public  class Program
    {
      public static void swap(ref int lhs,ref int rhs)
      {
          int temp = lhs;
          lhs = rhs;
          rhs = temp;
      }
          static void Main(string[] args)
        {
            int a = 10;
            int b = 80;

  Console.WriteLine("a is before sort " + a);
            Console.WriteLine("b is before sort " + b);
            swap(ref a, ref b);
            Console.WriteLine("");
            Console.WriteLine("a is after sort " + a);
            Console.WriteLine("b is after sort " + b);  
        }
    }
}
...