Передача объектов по ссылке или значению в C#

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

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

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

Почему это?

7 ответов


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

Если вы хотите использовать pass-by-reference, вы должны использовать 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(...);
}

у меня есть статья, которая идет в гораздо более подробно в этом. В принципе, "пройти по ссылке" не означает то, что вы думаете, что это значит.


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

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


Я думаю, это яснее, когда вы делаете это так. Я рекомендую скачать LinkPad чтобы проверить такие вещи.

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

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

UpdateImplicitly

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

UpdateExplicitly

имя: Фавио, фамилия: Бесерра!--4-->


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

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

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
}

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

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

следующая ссылка даст вам лучшую идею.

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


в Pass by Reference вы добавляете только " ref " в параметрах функции и один более того, вы должны объявлять функцию "static" из-за main is static (#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);  
        }
    }
}

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

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

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

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

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

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

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

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