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ê?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.
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
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
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
}
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
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);
}
}
}
" 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."