Passagem de Objectos por referência ou valor em C#

Em C#, Sempre pensei que as variáveis não primitivas eram passadas pela referência e os valores primitivos passavam pelo valor.

Então, ao passar a um método qualquer objeto não primitivo, qualquer coisa feita ao objeto no método afetaria o objeto sendo passado. (C # 101 Coisas)

No entanto, reparei nisso quando passo por um sistema.Desenho.Objeto de imagem, que este não parece ser o caso? Se eu passar por um sistema.desenho.objecto de imagem para outro método, e carregar uma imagem nesse objeto, então deixe esse método sair do escopo e voltar para o método de chamada, essa imagem não é carregada no objeto original?

Porquê?

Author: nawfal, 2012-01-03

7 answers

Os objectos não passaram de todo. Por padrão, o argumento é avaliado e seu valor é passado, por valor, como o valor inicial do parâmetro do método que você está chamando. Agora o ponto importante é que o valor é uma referência para tipos de referência - uma forma de chegar a um objeto (ou nulo). As alterações a esse objeto serão visíveis a partir do chamador. No entanto, se alterar o valor do parâmetro para se referir a um objecto diferente não será visível quando estiver usando o valor pass by, Que é o padrão para todos os tipos.

Se quiser usar pass-by-reference, deve usar out ou ref, quer o tipo de parâmetro seja um tipo de valor ou um tipo de referência. Nesse caso, efetivamente a variável em si é passada por referência, de modo que o parâmetro usa o mesmo local de armazenamento que o argumento - e as mudanças no parâmetro em si são vistas pelo chamador.

Então:

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(...);
}
Tenho um artigo que vai muito longe. mais detalhes neste. Basicamente, "passar por referência" não significa o que pensas que significa.
 360
Author: Jon Skeet, 2012-01-03 06:24:57

Mais uma amostra de código para mostrar isto:

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";
}

E a saída:

TestPlain: 0

TestRef: 5

TestObjPlain: test

TestObjRef: TestObjRef

 12
Author: vmg, 2018-03-12 14:34:01
Acho que fica mais claro quando o fazes assim. Eu recomendo baixar LinkPad para testar coisas como esta.
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";
}

E isso deve sair

WontUpdate

Nome Próprio: egli, apelido: Becerra

Actualizar implicitamente

Primeiro nome: Favio, último nome: Becerra

UpdateExplicitly

Primeiro nome: Favio, último nome: Becerra

 3
Author: Egli Becerra, 2016-09-16 04:05:45

Quando passas o objecto System.Drawing.Image para um método, estás a passar uma cópia de referência para esse objecto.

Por isso, se dentro desse método estiver a carregar uma nova imagem, está a carregar com uma referência Nova/copiada. Não estás a fazer mudanças no original.

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
}
 3
Author: Haris Hasan, 2018-03-12 14:14:20
Como passaste o objecto ao método? Estás a fazer algo novo dentro desse método para o objecto? Em caso afirmativo, deve utilizar ref em método. Seguir o link dá-te uma ideia melhor.

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

 2
Author: dotnetstep, 2018-03-12 14:13:44

Em Pass By Reference só adiciona " ref " nos parâmetros da função e um mais coisas que você deve estar declarando a função "estática" por causa do main é estática (#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);  
        }
    }
}
 0
Author: user5593590, 2016-04-27 02:07:04
A resposta aceite parece um pouco errada e confusa. O que é uma "cópia de uma referência?" Como faz sentido a seguinte declaração?:

" no entanto, a alteração do valor do parâmetro para se referir a um objecto diferente não será visível quando estiver a usar o pass by value, que é o padrão para todos os tipos."Pass by value não é o padrão para todos os tipos.

Seu exemplo em seu link tenta definir uma instância de um objeto para nulo. O objecto não era foi definido com sucesso como nulo devido à recolha automática de lixo. Não pode ser apagado desta forma.

Aqui está um artigo da Microsoft que compara Java e c#.

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

" Todos os objectos são referências

Os tipos de referência são muito semelhantes aos ponteiros em C++, particularmente ao definir um identificador para uma nova instância de classe. Mas ao acessar as Propriedades ou métodos deste tipo de referência, use o"." operador, que é semelhante a acessar instâncias de dados em C++ que são criadas na pilha. Todas as instâncias de classe são criadas no heap usando o novo operador, mas o delete não é permitido, já que ambas as linguagens usam seus próprios esquemas de coleta de lixo, discutidos abaixo."

 0
Author: user3198764, 2016-12-26 10:35:55