Qual é a diferença entre as palavras-chave ‘ref’ e ‘out’?

Estou criando uma function onde preciso passar um object para que ele possa ser modificado pela function. Qual é a diferença entre:

public void myFunction(ref MyClass someClass) 

e

 public void myFunction(out MyClass someClass) 

Qual devo usar e por quê?

ref informa ao compilador que o object é inicializado antes de entrar na function, enquanto out informa ao compilador que o object será inicializado dentro da function.

Então, enquanto ref é two-ways, out é apenas out.

O modificador ref significa que:

  1. O valor já está definido e
  2. O método pode ler e modificar isso.

O modificador out significa que:

  1. O valor não está definido e não pode ser lido pelo método até que seja definido.
  2. O método deve configurá-lo antes de retornar.

Digamos que Dom apareça no cubículo de Peter sobre o memorando sobre os relatórios do TPS.

Se Dom fosse um argumento de ref, ele teria uma cópia impressa do memorando.

Se Dom fosse um argumento fora, ele faria Peter imprimir uma nova cópia do memorando para ele levar com ele.

Eu vou tentar minha mão em uma explicação:

Acho que entendemos como os tipos de valor funcionam corretamente? Tipos de valor são (int, long, struct etc.). Quando você os envia para uma function sem um comando ref, copia os dados . Tudo o que você faz com esses dados na function afeta apenas a cópia, não o original. O comando ref envia os dados ACTUAL e quaisquer alterações afetarão os dados fora da function.

Ok, para a parte confusa, tipos de referência:

Vamos criar um tipo de referência:

 List someobject = new List() 

Quando você atualiza algum object , duas partes são criadas:

  1. O bloco de memory que contém dados para algum object .
  2. Uma referência (ponteiro) para esse bloco de dados.

Agora, quando você envia someobject em um método sem ref, COPIE o ponteiro de referência , NÃO os dados. Então você agora tem isso:

 (outside method) reference1 => someobject (inside method) reference2 => someobject 

Duas referências apontando para o mesmo object. Se você modificar uma propriedade em algum object usando reference2, ela afetará os mesmos dados apontados por reference1.

  (inside method) reference2.Add("SomeString"); (outside method) reference1[0] == "SomeString" //this is true 

Se você nula a referência2 ou aponta para novos dados, ela não afetará a referência1 nem a referência de dados1 aponta para.

 (inside method) reference2 = new List(); (outside method) reference1 != null; reference1[0] == "SomeString" //this is true The references are now pointing like this: reference2 => new List() reference1 => someobject 

Agora o que acontece quando você envia someobject por ref para um método? A referência real para someobject é enviada para o método. Então você agora tem apenas uma referência aos dados:

 (outside method) reference1 => someobject; (inside method) reference1 => someobject; 

Mas o que isso significa? Ele age exatamente da mesma forma que enviar algum object não por ref, exceto por duas coisas principais:

1) Quando você anula a referência dentro do método, será nulo aquele fora do método.

  (inside method) reference1 = null; (outside method) reference1 == null; //true 

2) Agora você pode apontar a referência para um local de dados completamente diferente e a referência fora da function agora apontará para o novo local de dados.

  (inside method) reference1 = new List(); (outside method) reference1.Count == 0; //this is true 

ref está dentro e fora .

Você deve usar de preferência onde quer que seja suficiente para suas necessidades.

Fora:

Em C #, um método pode retornar apenas um valor. Se você quiser retornar mais de um valor, poderá usar a palavra-chave out. O modificador de saída retorna como retorno por referência. A resposta mais simples é que a palavra-chave “out” é usada para obter o valor do método.

  1. Você não precisa inicializar o valor na function de chamada.
  2. Você deve atribuir o valor na function chamada, caso contrário, o compilador reportará um erro.

ref:

Em C #, quando você passa um tipo de valor como int, float, double etc. como um argumento para o parâmetro method, ele é passado por valor. Portanto, se você modificar o valor do parâmetro, ele não afetará o argumento na chamada do método. Mas se você marcar o parâmetro com a palavra-chave “ref”, ele refletirá na variável real.

  1. Você precisa inicializar a variável antes de chamar a function.
  2. Não é obrigatório atribuir qualquer valor ao parâmetro ref no método. Se você não alterar o valor, qual é a necessidade de marcá-lo como “ref”?

Estendendo o cão, exemplo Cat. O segundo método com ref altera o object referenciado pelo chamador. Daí “gato” !!!

  public static void Foo() { MyClass myObject = new MyClass(); myObject.Name = "Dog"; Bar(myObject); Console.WriteLine(myObject.Name); // Writes "Dog". Bar(ref myObject); Console.WriteLine(myObject.Name); // Writes "Cat". } public static void Bar(MyClass someObject) { MyClass myTempObject = new MyClass(); myTempObject.Name = "Cat"; someObject = myTempObject; } public static void Bar(ref MyClass someObject) { MyClass myTempObject = new MyClass(); myTempObject.Name = "Cat"; someObject = myTempObject; } 

Como você está passando um tipo de referência (uma class), não há necessidade de usar ref porque, por padrão, apenas uma referência ao object real é passada e, portanto, você sempre altera o object por trás da referência.

Exemplo:

 public void Foo() { MyClass myObject = new MyClass(); myObject.Name = "Dog"; Bar(myObject); Console.WriteLine(myObject.Name); // Writes "Cat". } public void Bar(MyClass someObject) { someObject.Name = "Cat"; } 

Contanto que você passe em uma class, você não precisa usar o ref se quiser alterar o object dentro do seu método.

ref e out se comportam de maneira similar, exceto as seguintes diferenças.

  • ref variável ref deve ser inicializada antes do uso. out variável pode ser usada sem atribuição
  • out parâmetro out deve ser tratado como um valor não atribuído pela function que o utiliza. Assim, podemos usar out parâmetro initialized out no código de chamada, mas o valor será perdido quando a function for executada.

“Padeiro”

Isso porque o primeiro altera sua referência de string para apontar para “Baker”. A alteração da referência é possível porque você a transmitiu por meio da palavra-chave ref (=> uma referência a uma referência a uma string). A segunda chamada obtém uma cópia da referência à string.

string parece algum tipo de especial no começo. Mas string é apenas uma class de referência e se você definir

 string s = "Able"; 

então s é uma referência a uma class de string que contém o texto “Able”! Outra atribuição para a mesma variável via

 s = "Baker"; 

não altera a string original, apenas cria uma nova instância e mostra a instância!

Você pode tentar com o seguinte pequeno exemplo de código:

 string s = "Able"; string s2 = s; s = "Baker"; Console.WriteLine(s2); 

O que você espera? O que você vai conseguir ainda é “Capaz” porque você acabou de definir a referência em s para outra instância enquanto s2 aponta para a instância original.

EDIT: string também é imutável, o que significa que simplesmente não existe nenhum método ou propriedade que modifique uma instância de string existente (você pode tentar encontrar um nos docs, mas você não irá terminar nenhum :-)). Todos os methods de manipulação de string retornam uma nova instância de string! (É por isso que você geralmente obtém um melhor desempenho ao usar a class StringBuilder)

Out: Uma instrução de retorno pode ser usada para retornar apenas um valor de uma function. No entanto, usando parâmetros de saída, você pode retornar dois valores de uma function. parameters de saída são como parâmetros de referência, exceto que eles transferem dados do método em vez de transferi-los.

O exemplo a seguir ilustra isso:

 using System; namespace CalculatorApplication { class NumberManipulator { public void getValue(out int x ) { int temp = 5; x = temp; } static void Main(string[] args) { NumberManipulator n = new NumberManipulator(); /* local variable definition */ int a = 100; Console.WriteLine("Before method call, value of a : {0}", a); /* calling a function to get the value */ n.getValue(out a); Console.WriteLine("After method call, value of a : {0}", a); Console.ReadLine(); } } } 

ref: Um parâmetro de referência é uma referência a um local de memory de uma variável. Quando você passa parâmetros por referência, diferente dos parâmetros de valor, um novo local de armazenamento não é criado para esses parâmetros. Os parâmetros de referência representam o mesmo local de memory que os parâmetros reais fornecidos ao método.

Em C #, você declara os parâmetros de referência usando a palavra-chave ref. O exemplo a seguir demonstra isso:

 using System; namespace CalculatorApplication { class NumberManipulator { public void swap(ref int x, ref int y) { int temp; temp = x; /* save the value of x */ x = y; /* put y into x */ y = temp; /* put temp into y */ } static void Main(string[] args) { NumberManipulator n = new NumberManipulator(); /* local variable definition */ int a = 100; int b = 200; Console.WriteLine("Before swap, value of a : {0}", a); Console.WriteLine("Before swap, value of b : {0}", b); /* calling a function to swap the values */ n.swap(ref a, ref b); Console.WriteLine("After swap, value of a : {0}", a); Console.WriteLine("After swap, value of b : {0}", b); Console.ReadLine(); } } } 

ref e out funcionam como passar por referências e passar por pointers como em C ++.

Para ref, o argumento deve declarado e inicializado.

Para fora, o argumento deve declarado, mas pode ou não ser inicializado

  double nbr = 6; // if not initialized we get error double dd = doit.square(ref nbr); double Half_nbr ; // fine as passed by out, but inside the calling method you initialize it doit.math_routines(nbr, out Half_nbr); 

Para aqueles que aprendem pelo exemplo (como eu), aqui está o que Anthony Kolesov está dizendo .

Eu criei alguns exemplos mínimos de ref, out e outros para ilustrar o ponto. Eu não estou cobrindo as melhores práticas, apenas exemplos para entender as diferenças.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

ref significa que o valor no parâmetro ref já está definido, o método pode lê-lo e modificá-lo. Usar a palavra-chave ref é o mesmo que dizer que o chamador é responsável por inicializar o valor do parâmetro.


out informa ao compilador que a boot do object é de responsabilidade da function, a function deve ser atribuída ao parâmetro out. Não é permitido deixá-lo sem atribuição.

Leia aqui

Tempo de criação:

(1) Criamos o método de chamada Main()

(2) cria um object List (que é um object de tipo de referência) e o armazena na variável myList .

 public sealed class Program { public static Main() { List myList = new List(); 

Durante o tempo de execução:

(3) O tempo de execução aloca uma memory na pilha em # 00, larga o suficiente para armazenar um endereço (# 00 = myList , já que os nomes das variables ​​são na verdade apenas aliases para locais de memory)

(4) Runtime cria um object de lista na pilha no local de memory #FF (todos esses endereços são, por exemplo, sakes)

(5) Runtime, em seguida, armazenaria o endereço inicial #FF do object em # 00 (ou em palavras, armazena a referência do object List no ponteiro myList )

Voltar ao Horário de Autoria:

(6) Em seguida, passamos o object List como argumento myParamList para o método chamado modifyMyList e atribuímos um novo object List a ele

 List myList = new List(); List newList = ModifyMyList(myList) public List ModifyMyList(List myParamList){ myParamList = new List(); return myParamList; } 

Durante o tempo de execução:

(7) O tempo de execução inicia a rotina de chamada do método chamado e, como parte dele, verifica o tipo de parâmetros.

(8) Ao encontrar o tipo de referência, ele aloca uma memory na pilha em # 04 para o aliasing da variável de parâmetro myParamList .

(9) Ele então armazena o valor #FF nele também.

(10) O tempo de execução cria um object de lista na pilha no local de memory # 004 e substitui #FF em # 04 por esse valor (ou desreferencia o object List original e apontou para o novo object List nesse método)

O endereço no # 00 não é alterado e mantém a referência para #FF (ou o ponteiro myList original não é perturbado).


A palavra-chave ref é uma diretiva de compilador para ignorar a geração de código de tempo de execução para (8) e (9), o que significa que não haverá alocação de heap para parâmetros de método. Ele usará o ponteiro # 00 original para operar no object em #FF. Se o ponteiro original não for inicializado, o tempo de execução parará de reclamar, não será possível prosseguir, pois a variável não foi inicializada

A palavra chave out é uma diretiva de compilador que é praticamente a mesma que ref com uma pequena modificação em (9) e (10). O compilador espera que o argumento seja não inicializado e continuará com (8), (4) e (5) para criar um object na pilha e para armazenar seu endereço inicial na variável de argumento. Nenhum erro não inicializado será lançado e qualquer referência anterior armazenada será perdida.

Eles são praticamente os mesmos – a única diferença é que uma variável que você passa como um parâmetro de saída não precisa ser inicializada, e o método que usa o parâmetro ref deve defini-la como algo.

 int x; Foo(out x); // OK int y; Foo(ref y); // Error 

Os parâmetros de referência são para dados que podem ser modificados, parâmetros de saída são para dados que são uma saída adicional para a function (por exemplo, int.TryParse) que já estão usando o valor de retorno para algo.

  public static void Main(string[] args) { //int a=10; //change(ref a); //Console.WriteLine(a); // Console.Read(); int b; change2(out b); Console.WriteLine(b); Console.Read(); } // static void change(ref int a) //{ // a = 20; //} static void change2(out int b) { b = 20; } 

você pode checar esse código ele irá descrever sua diferença completa quando você usa “ref” significa que você já inicializa o int / string

mas quando você usa “out” funciona em ambas as condições quando você inicializa int / string ou não, mas vc deve inicializar o int / string nessa function

Ref: A palavra-chave ref é usada para passar um argumento como referência. Isso significa que quando o valor desse parâmetro é alterado no método, ele é refletido no método de chamada. Um argumento que é passado usando uma palavra-chave ref deve ser inicializado no método de chamada antes de ser passado para o método chamado.

Out: A palavra-chave out também é usada para passar um argumento como ref keyword, mas o argumento pode ser passado sem atribuir nenhum valor a ele. Um argumento que é passado usando uma palavra-chave out deve ser inicializado no método chamado antes de retornar ao método de chamada.

 public class Example { public static void Main() { int val1 = 0; //must be initialized int val2; //optional Example1(ref val1); Console.WriteLine(val1); Example2(out val2); Console.WriteLine(val2); } static void Example1(ref int value) { value = 1; } static void Example2(out int value) { value = 2; } } /* Output 1 2 

Ref e out em sobrecarga de método

Ambos ref e out não podem ser usados ​​em sobrecarga de método simultaneamente. No entanto, ref e out são tratados de maneira diferente em tempo de execução, mas são tratados da mesma forma em tempo de compilation (CLR não diferencia entre os dois enquanto cria IL para ref e out).

Abaixo mostrei um exemplo usando Ref e out . Agora, todos vocês serão liberados sobre ref e out.

No exemplo abaixo mencionado quando eu comentar // myRefObj = new myClass {Name = “ref outside called !!”}; linha, receberá um erro dizendo “Uso da variável local não atribuída ‘myRefObj'” , mas não há tal erro em fora .

Onde usar Ref : quando estamos chamando um procedimento com um parâmetro in e o mesmo parâmetro será usado para armazenar a saída desse proc.

Onde usar Out: quando estamos chamando um procedimento sem no parâmetro e o mesmo param será utilizado para retornar o valor daquele proc. Observe também a saída

 public partial class refAndOutUse : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { myClass myRefObj; myRefObj = new myClass { Name = "ref outside called!! 
" }; myRefFunction(ref myRefObj); Response.Write(myRefObj.Name); //ref inside function myClass myOutObj; myOutFunction(out myOutObj); Response.Write(myOutObj.Name); //out inside function } void myRefFunction(ref myClass refObj) { refObj.Name = "ref inside function
"; Response.Write(refObj.Name); //ref inside function } void myOutFunction(out myClass outObj) { outObj = new myClass { Name = "out inside function
" }; Response.Write(outObj.Name); //out inside function } } public class myClass { public string Name { get; set; } }

Do ponto de vista de um método que recebe um parâmetro, a diferença entre ref e out é que C # requer que os methods gravem em todos out parâmetros out antes de retornar, e não deve fazer nada com esse parâmetro, além de passá-lo como um parâmetro out ou escrever para ele, até que tenha sido passado como um parâmetro out para outro método ou escrito diretamente. Note que algumas outras línguas não impõem tais requisitos; Um método virtual ou de interface que é declarado em C # com um parâmetro out pode ser substituído em outro idioma que não imponha restrições especiais a tais parâmetros.

Do ponto de vista do chamador, o C # assumirá, em muitas circunstâncias, quando chamar um método com um parâmetro out fará com que a variável transmitida seja gravada sem ter sido lida primeiro. Essa suposição pode não estar correta ao chamar methods escritos em outros idiomas. Por exemplo:

 struct MyStruct { ... myStruct(IDictionary d) { d.TryGetValue(23, out this); } } 

If myDictionary identifica uma implementação IDictionary escrita em um idioma diferente de C #, mesmo que MyStruct s = new MyStruct(myDictionary); parece uma atribuição, poderia potencialmente não ser modificada.

Observe que os construtores escritos em VB.NET, diferentemente dos construtores em C #, não fazem suposições sobre se os methods chamados modificarão qualquer parâmetro out e limparão todos os campos incondicionalmente. O comportamento estranho aludido acima não ocorrerá com código escrito inteiramente em VB ou inteiramente em C #, mas pode ocorrer quando o código escrito em C # chama um método escrito em VB.NET.

Se você quiser passar seu parâmetro como um ref, então você deve inicializá-lo antes de passar o parâmetro para a function, caso contrário, o próprio compilador mostrará o erro. Mas no caso do parâmetro out você não precisa inicializar o parâmetro object antes de passá-lo para o Você pode inicializar o object no próprio método de chamada.

Eu estava jogando com o ref e achei este exemplo bastante interessante. Eu pensei que a chamada de RefEater(ref s1); causará erro de compilation como no segundo caso comentado, mas o campo s1 é inicializado com seu valor padrão antes que o construtor seja chamado ( https://stackoverflow.com/a/1920659/5612780 ).

 public class Class1 { // this will have the default value private string s1; public Class1() { // no issue here.. RefEater(ref s1); // Error CS0165 Use of unassigned local variable 's2' //string s2; //RefEater(ref s2); } private void RefEater(ref string s) { } } 

Eu posso não ser tão bom nisso, mas certamente as strings (mesmo que sejam tecnicamente tipos de referência e vivem na pilha) são passadas por valor, não por referência?

  string a = "Hello"; string b = "goodbye"; b = a; //attempt to make b point to a, won't work. a = "testing"; Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!! 

É por isso que você precisa de ref, se você quer que as mudanças existam fora do escopo da function que as faz, você não está passando uma referência de outra forma.

Tanto quanto sei, você só precisa de ref para structs / value types e string propriamente dito, já que string é um tipo de referência que finge que é, mas não é um tipo de valor.

Eu posso estar completamente errado aqui, eu sou novo.

Tenha em mente que o parâmetro de referência que é passado dentro da function é trabalhado diretamente.

Por exemplo,

  public class MyClass { public string Name { get; set; } } public void Foo() { MyClass myObject = new MyClass(); myObject.Name = "Dog"; Bar(myObject); Console.WriteLine(myObject.Name); // Writes "Dog". } public void Bar(MyClass someObject) { MyClass myTempObject = new MyClass(); myTempObject.Name = "Cat"; someObject = myTempObject; } 

Isto irá escrever Dog, não Cat. Portanto, você deve trabalhar diretamente em algum object.