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


892

Estou criando uma função na qual preciso passar um objeto para que possa ser modificado pela função. Qual é a diferença entre:

public void myFunction(ref MyClass someClass)

e

public void myFunction(out MyClass someClass)

Qual devo usar e por quê?


69
Você: Eu preciso passar um objeto para que ele possa ser modificado . Parece que MyClassseria um classtipo, ou seja, um tipo de referência. Nesse caso, o objeto que você passa pode ser modificado pelo myFunctionmesmo sem ref/ outpalavra-chave. myFunctionreceberá uma nova referência que aponta para o mesmo objeto e pode modificar o mesmo objeto o quanto desejar. A diferença que a refpalavra - chave faria seria myFunctionreceber a mesma referência ao mesmo objeto. Isso seria importante apenas se myFunctionmudássemos a referência para apontar para outro objeto.
Jeppe Stig Nielsen

3
Estou intrigado com a quantidade de respostas confusas aqui, quando o @ AnthonyKolesov's é bastante perfeito.
o0 '.

Declarar um método out é útil quando você deseja que um método retorne vários valores. Um argumento pode ser atribuído a nulo. Isso permite que os métodos retornem valores opcionalmente.
Yevgraf Andreyevich Zhivago

Aqui explicado com o exemplo É mais compreensível :) dotnet-tricks.com/Tutorial/csharp/…
Prageeth godage

2
O comentário de @ JeppeStigNielsen é, tecnicamente, a (única) resposta correta à pergunta real do OP. Para passar um objeto para um método para que o método possa modificá-lo , basta passar o (referência ao) objeto para o método por valor. Alterar o objeto dentro do método por meio do argumento object modifica o objeto original , mesmo que o método contenha sua própria variável separada (que faz referência ao mesmo objeto).
David R Tribble

Respostas:


1162

refinforma ao compilador que o objeto foi inicializado antes de inserir a função, enquanto outinforma ao compilador que o objeto será inicializado dentro da função.

Então, enquanto refé bidirecional, outé somente fora.


270
Outra coisa interessante específica de out é que a função precisa ser atribuída ao parâmetro out. Não é permitido deixá-lo sem atribuição.
Daniel Earwicker

7
'ref' é aplicável apenas ao tipo de valor? Como o tipo de referência é sempre passado por ref.
defeituoso

3
Sim. Tipos de valor, incluindo estruturas
Rune Grimstad

17
@faulty: Não, ref não se aplica apenas a tipos de valor. ref / out são como ponteiros em C / C ++, eles lidam com a localização da memória do objeto (indiretamente em C #) em vez do objeto direto.
thr

52
@faulty: Contraintuitivamente, os tipos de referência sempre são passados ​​por valor em C #, a menos que você use o especificador ref. Se você definir myval = somenewval, o efeito será apenas nesse escopo da função. A palavra-chave ref permitirá que você altere myval para apontar para somenewval.
JasonTrue

535

O refmodificador significa que:

  1. O valor já está definido e
  2. O método pode ler e modificá-lo.

O outmodificador significa que:

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

30
Esta resposta explica de maneira mais clara e concisa as restrições que o compilador impõe ao usar a palavra-chave out em oposição à palavra-chave ref.
Aprendiz do Dr. Wily

5
No MSDN: um parâmetro ref deve ser inicializado antes do uso, enquanto um parâmetro out não precisa ser explicitamente inicializado antes de ser passado e qualquer valor anterior é ignorado.
Shiva Kumar

1
Com out, ele pode ser lido dentro do método, antes de ser definido por esse método, se tiver sido inicializado antes da chamada do método? Quero dizer, o método chamado pode ler o que o método de chamada passou para ele como argumento?
Panzercrisis

3
Panzercrisis, para "out", o método chamado pode ler se já estiver definido. mas deve configurá-lo novamente.
precisa saber é o seguinte

146

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

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

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


54
ref Dom teria escrito o relatório a lápis para que Peter poderia modificá-lo
Deebster

6
@ Deebster, você sabe, essa metáfora nunca fez nada com você, por que você deve torturá-la? ;)
Michael Blackburn

21
divertido, mas educado, o stackoverflow precisa de mais posts como este #
Frank Visaggio

2
Caso alguém ache essa resposta apenas meio engraçada, assista ao filme "Office Space".
displayName

e Dom e Peters patrão estaria behin Dom (como argumento para fora), forçando ambos a trabalhar em imprimi-lo novamente até que Peter mãos Domd a impressão
Patrick Artner

57

Vou tentar uma explicação:

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

Aprovação para a parte confusa, tipos de referência:

Vamos criar um tipo de referência:

List<string> someobject = new List<string>()

Quando você cria um novo objeto , duas partes são criadas:

  1. O bloco de memória que contém dados para algum objeto .
  2. Uma referência (ponteiro) para esse bloco de dados.

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

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

Duas referências apontando para o mesmo objeto. Se você modificar uma propriedade em algum objeto usando a referência2, ela afetará os mesmos dados apontados pela referência1.

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

Se você anular a referência2 ou apontá-la para novos dados, ela não afetará a referência1 nem a referência1 dos dados.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Agora, o que acontece quando você envia algum objeto por ref para um método? A referência real a algum objeto é enviada ao método. Então agora você 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 o envio de algum objeto, não por ref, exceto por duas coisas principais:

1) Quando você anula a referência dentro do método, ela anula a referência 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 função agora apontará para o novo local de dados.

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

Afinal de contas (no caso de ref), há apenas uma referência aos dados, mas dois apelidos para eles. Direita?
Sadiq

3
Votado para a explicação clara. Mas acho que isso não responde à pergunta, pois não explica a diferença entre refe outparâmetros.
precisa saber é o seguinte

1
Surpreendente. você pode explicar o mesmo que para a outpalavra-chave?
Asif Mushtaq

28

ref está dentro e fora .

Você deve usar outde preferência onde for suficiente para seus requisitos.


não exatamente, pois a resposta aceita ref se direcional e inútil, ignorando os tipos de valor, se não for desdobrada.
Kenny

@kenny: Você pode esclarecer um pouco, por exemplo - quais palavras você mudaria para manter o espírito da resposta, mas remover a imprecisão que perceber? Minha resposta não é um palpite maluco de um novato, mas a pressa (discrepância, erros de digitação) em seu comentário parece assumir que sim. O objetivo é fornecer uma maneira de pensar sobre a diferença com o menor número de palavras.
Ruben Bartelink

(BTW eu estou familiarizado com os tipos de valor, tipos de referência, passando por referência, passando por valor, COM e C ++ se você achar que é útil para fazer referência a esses conceitos no seu esclarecimento)
Ruben Bartelink

1
As referências a objetos são passadas por valor (exceto ao usar a palavra-chave "ref" ou "out"). Pense nos objetos como números de identificação. Se uma variável de classe contiver "Objeto # 1943" e alguém passar essa variável por valor para uma rotina, essa rotina poderá fazer alterações no Objeto # 1943, mas não poderá fazer com que a variável aponte para algo diferente de "Objeto # 1943". Se a variável foi passada por referência, a rotina pode fazer com que o ponto variável retenha "Objeto # 5441".
Supercat 15/12

1
@ supercat: Eu gosto da sua explicação sobre ref vs val (e essa anaologia de acompanhamento). Eu acho que o Kenny não precisa realmente de nada disso explicado a ele, (relativamente) por mais confusos que sejam seus comentários. Eu gostaria que todos pudéssemos remover esses malditos comentários, pois eles estão apenas confundindo todo mundo. A causa raiz de todo esse absurdo parece ser que Kenny interpretou mal minha resposta e ainda precisa apontar uma única palavra que deve ser adicionada / removida / substituída. Nenhum de nós aprendemos nada com a discussão que já não conhecíamos e a outra resposta tem um número ridículo de votos positivos.
Ruben Bartelink

18

Fora:

Em C #, um método pode retornar apenas um valor. Se você deseja retornar mais de um valor, pode usar a palavra-chave out. O modificador out 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 função de chamada.
  2. Você deve atribuir o valor na função chamada, caso contrário, o compilador relatará um erro.

ref:

Em C #, quando você passa um tipo de valor como int, float, double etc. como argumento para o parâmetro do método, 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 função.
  2. Não é obrigatório atribuir nenhum valor ao parâmetro ref no método Se você não alterar o valor, qual é a necessidade de marcá-lo como "ref"?

"No C #, um método pode retornar apenas um valor. Se você deseja retornar mais de um valor, pode usar a palavra-chave out." Também podemos usar "ref" para retornar valor. Então, podemos usar ref e out se quisermos retornar vários valores de um método?
Ned

1
No c # 7, você pode retornar vários valores com ValueTuples.
Iman Bahrampour

13

Estendendo o cão, exemplo do gato. O segundo método com ref altera o objeto 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;
    }

8

Como você está passando um tipo de referência (uma classe), não há necessidade de usá-lo refporque, por padrão, apenas uma referência ao objeto real é passada e, portanto, você sempre altera o objeto atrá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";
}

Desde que você passe em uma classe, não precisará usá- reflo se quiser alterar o objeto dentro do seu método.


5
Isso funciona apenas se nenhum novo objeto for criado e retornado. Quando um novo objeto é criado, a referência ao objeto antigo seria perdida.
Etsuba

8
Isso está errado - tente o seguinte: add someObject = nullto Barend execute. Seu código funcionará bem, pois apenas Bara referência à instância foi anulada. Agora mude Barpara Bar(ref MyClass someObject)e executar novamente - você vai ter uma NullReferenceExceptioncausa Foode referência 's para a instância foi anulada também.
Keith

8

refe outse comportam de maneira semelhante, exceto as seguintes diferenças.

  • refA variável deve ser inicializada antes do uso. outvariável pode ser usada sem atribuição
  • outO parâmetro deve ser tratado como um valor não atribuído pela função que o utiliza. Portanto, podemos usar o outparâmetro inicializado no código de chamada, mas o valor será perdido quando a função for executada.


6

"Padeiro"

Isso ocorre porque o primeiro altera sua referência de string para apontar para "Baker". A alteração da referência é possível porque você a passou 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 a princípio. Mas string é apenas uma classe de referência e se você definir

string s = "Able";

então s é uma referência a uma classe de string que contém o texto "Capaz"! Outra atribuição para a mesma variável via

s = "Baker";

não altera a string original, mas apenas cria uma nova instância e vamos apontar para essa instância!

Você pode experimentá-lo 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ê obterá 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 há método ou propriedade que modifique uma instância de string existente (você pode tentar encontrar uma nos documentos, mas não consegue :-)). Todos os métodos 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 classe StringBuilder)


1
Exatamente. Portanto, não é estritamente verdade dizer "Como você está passando um tipo de referência (uma classe), não há necessidade de usar ref".
Paul Mitchell

Em teoria, é certo dizer isso porque ele escreveu "para que possa ser modificado", o que não é possível em strings. Mas por causa de objetos imutáveis, "ref" e "out" são muito úteis também para tipos de referência! (.Net contém muitas classes imutáveis!)
mmmmmmmm

Sim, você está certo. Não pensei em objetos imutáveis ​​como cordas, porque a maioria dos objetos é mutável.
Albic 23/12/08

1
Bem, esta é uma resposta intrigante para ver no LQP, com certeza; não há nada de errado com isso, exceto que parece ser uma resposta longa e completa a outro comentário (já que a pergunta original menciona Able e Baker em nenhuma de suas revisões), como se fosse um fórum. Acho que isso ainda não estava resolvido há muito tempo.
Nathan Tuggy

6

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 inicialização do objeto é de responsabilidade da função, a função deve ser atribuída ao parâmetro out. Não é permitido deixá-lo sem atribuição.


5

Out: Uma declaração de retorno pode ser usada para retornar apenas um valor de uma função. No entanto, usando parâmetros de saída, você pode retornar dois valores de uma função. Os parâmetros de saída são como parâmetros de referência, exceto que eles transferem dados do método e não para ele.

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 memória de uma variável. Quando você passa parâmetros por referência, diferentemente 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 da memória 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();

      }
   }
}

4

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

Para ref, o argumento deve ser declarado e inicializado.

Para fora, o argumento deve ser 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);

1
Você pode declarar uma linha variável: out double Half_nbr.
Sebastian Hofmann

4

Tempo de criação:

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

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

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

Durante o tempo de execução:

(3) O tempo de execução aloca uma memória na pilha em # 00, larga o suficiente para armazenar um endereço (# 00 = myList, pois os nomes de variáveis ​​são realmente apenas aliases para locais de memória)

(4) O tempo de execução cria um objeto de lista na pilha no local de memória #FF (todos esses endereços são, por exemplo, sakes)

(5) O tempo de execução armazenaria o endereço inicial #FF do objeto em # 00 (ou, em palavras, armazena a referência do objeto List no ponteiro myList)

Voltar ao tempo de criação:

(6) Passamos o objeto List como argumento myParamListao método chamado modifyMyListe atribuímos um novo objeto List a ele.

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Durante o tempo de execução:

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

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

(9) Em seguida, ele também armazena o valor #FF.

(10) O tempo de execução cria um objeto de lista no heap no local de memória # 004 e substitui #FF no # 04 por esse valor (ou desreferencia o objeto de lista original e aponta para o novo objeto de lista neste método)

O endereço em # 00 não é alterado e mantém a referência em #FF (ou o myListponteiro 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 os parâmetros do método. Ele usará o ponteiro nº 00 original para operar no objeto em #FF. Se o ponteiro original não for inicializado, o tempo de execução será interrompido, alegando que não pode 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 ligeira 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 objeto no heap e armazenar seu endereço inicial na variável de argumento. Nenhum erro não inicializado será gerado e qualquer referência anterior armazenada será perdida.


3

Além de permitir que você reatribua a variável de outra pessoa para uma instância diferente de uma classe, retorne vários valores, etc., usando refou outpermite que outra pessoa saiba o que você precisa dela e o que você pretende fazer com a variável que ela fornece

  • Você não precisa ref ou outse tudo o que você fará é modificar as coisas dentro da MyClassinstância que é passada no argumento someClass.

    • O método chamando vai ver mudanças como someClass.Message = "Hello World"se você usa ref, outou nada
    • A escrita someClass = new MyClass()interna myFunction(someClass)troca o objeto visto apenas someClassno escopo do myFunctionmétodo. O método de chamada ainda conhece a MyClassinstância original criada e transmitida ao seu método
  • Você precisa ref ou outplaneja trocar a someClasssaída por um objeto totalmente novo e deseja que o método de chamada veja sua alteração

    • Escrever someClass = new MyClass()dentro myFunction(out someClass)altera o objeto visto pelo método chamadomyFunction

Existem outros programadores

E eles querem saber o que você fará com os dados deles. Imagine que você está escrevendo uma biblioteca que será usada por milhões de desenvolvedores. Você quer que eles saibam o que você fará com as variáveis ​​deles quando chamarem seus métodos

  • Using reffaz uma declaração de "Passar uma variável atribuída a algum valor quando você chamar meu método. Esteja ciente de que eu posso alterá-la para algo totalmente diferente durante o curso do meu método. Não espere que sua variável aponte para o objeto antigo quando eu estiver pronto"

  • Using outfaz uma declaração de "Passar uma variável de espaço reservado para o meu método. Não importa se tem um valor ou não; o compilador me forçará a atribuí-lo a um novo valor. Garanto que o objeto apontado pelo seu variável antes de você chamar meu método, será diferente quando eu terminar

A propósito, no C # 7.2 também há um inmodificador

E isso impede que o método troque o passado na instância por uma instância diferente. Pense nisso como dizer aos milhões de desenvolvedores "me passe sua referência de variável original e prometo não trocar seus dados cuidadosamente criados por outra coisa". inpossui algumas peculiaridades e, em alguns casos, como onde uma conversão implícita pode ser necessária para tornar seu curta compatível com in into compilador temporariamente cria um int, amplia seu curta para ele, passa por referência e termina. Isso pode ser feito porque você declarou que não vai mexer com isso.


A Microsoft fez isso com os .TryParsemétodos nos tipos numéricos:

int i = 98234957;
bool success = int.TryParse("123", out i);

Ao sinalizar o parâmetro como outeles estão declarando ativamente aqui " definitivamente mudaremos seu valor meticulosamente criado de 98234957 para outra coisa"

É claro, eles meio que precisam, para coisas como analisar tipos de valor, porque se o método de análise não tivesse permissão para trocar o tipo de valor por outra coisa, não funcionaria muito bem .. Mas imagine que houvesse algum método fictício em alguns biblioteca que você está criando:

public void PoorlyNamedMethod(out SomeClass x)

Você pode ver que é um oute, portanto, pode saber que, se você passar horas processando números, criando o SomeClass perfeito:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Bem, isso foi uma perda de tempo, levando todas essas horas para fazer aquela aula perfeita. Definitivamente vai ser jogado fora e substituído por PoorlyNamedMethod


3

Para quem procura uma resposta concisa.

As palavras ref- outchave e são usadas para passar por reference.


Uma variável de refpalavra-chave deve ter um valor ou deve se referir a um objeto ou null antes de ser transmitido.


Diferentemente ref, uma variável de outpalavra-chave deve ter um valor ou deve se referir a um objeto ou null após sua passagem, assim como não há necessidade de ter um valor ou se referir a um objeto antes de passar.


2

Para ilustrar as excelentes explicações, desenvolvi o seguinte aplicativo de console:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/
  • AppendWorld: Uma cópia do StringListnome LiStrié passada. No início do método, esta cópia faz referência à lista original e, portanto, pode ser usada para modificar essa lista. Depois, faz LiStrireferência a outro List<string>objeto dentro do método que não afeta a lista original.

  • HalloWelt: LiStriRefé um alias do já inicializado ListStringRef. O List<string>objeto passado é usado para inicializar um novo, portanto, refera necessário.

  • CiaoMondo: LiStriOuté um alias ListStringOute deve ser inicializado.

Portanto, se um método apenas modificar o objeto referenciado pela variável passada, o compilador não permitirá que você use oute você não deve usá- reflo porque isso confundiria não o compilador, mas o leitor do código. Se o método fizer com que o argumento passado faça referência a outro objeto, use refpara um objeto já inicializado e outpara métodos que devem inicializar um novo objeto para o argumento passado. Além disso, refe outse comporta da mesma maneira.


1

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

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

Os parâmetros ref são para dados que podem ser modificados, e os parâmetros out são para dados que são uma saída adicional para a função (por exemplo, int.TryParse) que já está usando o valor de retorno para alguma coisa.


1

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

No exemplo abaixo mencionado, quando eu comento // myRefObj = new myClass {Name = "ref outside called !!"}; linha, receberá um erro dizendo "Uso de unassigned variável local 'myRefObj'" , mas não existe erro no para 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 no parâmetro e o mesmo parâmetro será usado para retornar o valor desse 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!!  <br/>" };
        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 <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 

1
 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 verificar esse código, ele descreverá sua diferença completa quando você usar "ref" significa que você já inicializou essa int / string

mas quando você usa "out", ele funciona nas duas condições em que você inicializa ou não essa int / string, mas você deve inicializar essa int / string nessa função


1

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 na sobrecarga do método

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


0

Do ponto de vista de um método que recebe um parâmetro, a diferença entre refe outé que o C # exige que os métodos gravem em todos os outparâmetros antes de retornar e não devem fazer nada com esse parâmetro, além de passá-lo como outparâmetro ou gravá-lo , até que tenha sido passado como outparâmetro para outro método ou gravado diretamente. Observe que alguns outros idiomas não impõem esses requisitos; um método virtual ou de interface declarado em C # com um outparâmetro pode ser substituído em outro idioma que não imponha restrições especiais a esses parâmetros.

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

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

Se myDictionaryidentificar uma IDictionary<TKey,TValue>implementação escrita em um idioma diferente de C #, mesmo que MyStruct s = new MyStruct(myDictionary);pareça uma atribuição, ela poderá não ser smodificada.

Observe que os construtores escritos no VB.NET, diferentemente dos do C #, não fazem suposições sobre se os métodos chamados modificarão algum outparâmetro e limparão todos os campos incondicionalmente. O comportamento estranho mencionado acima não ocorrerá com o 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.


0

Se você deseja passar seu parâmetro como um ref, você deve inicializá-lo antes de passar o parâmetro para a função, caso contrário o próprio compilador mostrará o erro. Você pode inicializar o objeto no próprio método de chamada.


-3

Lembre-se bem de que o parâmetro de referência que é passado dentro da função é diretamente trabalhado.

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

Isso escreverá Dog, não Cat. Portanto, você deve trabalhar diretamente em someObject.


6
Embora tudo aqui seja praticamente verdadeiro, isso realmente não explica a diferença entre por valor, por referência ou por fora. Na melhor das hipóteses, metade explica a diferença entre os tipos de referência e valor / imutável.
Conrad Frix

Se você deseja que esse código escreva cat, passe esse objeto junto com a tecla 'ref' desta forma: public static void Bar (ref MyClass someObject), Bar (ref myObject);
Daniel Botero Correa

-4

Talvez eu não seja tão bom nisso, mas certamente as strings (mesmo que sejam tecnicamente tipos de referência e morem 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ê deseja que as alterações existam fora do escopo da função que as cria, você não está passando uma referência de outra forma.

Tanto quanto sei, você só precisa de ref para structs / tipos de valor e string em si, pois string é um tipo de referência que finge que é, mas não é um tipo de valor.

Eu poderia estar completamente errado aqui, porém, sou novo.


5
Bem-vindo ao Stack Overflow, Edwin. As strings são passadas por referência, como qualquer outro objeto, tanto quanto eu sei. Você pode ficar confuso porque as strings são objetos imutáveis; portanto, não é tão óbvio que elas são passadas por referência. Imagine que a string tenha um método chamado Capitalize()que altere seu conteúdo para maiúsculas. Se você substituísse sua linha a = "testing";por a.Capitalize();, sua saída seria "OLÁ!", Não "Olá". Uma das vantagens dos tipos imutáveis ​​é que você pode passar referências e não se preocupar com outro código alterando o valor.
Don Kirkby

2
Existem três tipos fundamentais de semântica que um tipo pode expor: semântica de referência mutável, semântica de valor mutável e semântica imutável. Considere as variáveis ​​xey de um tipo T, que possui campo ou propriedade m, e suponha que x seja copiado para y. Se T tiver semântica de referência, as alterações em xm serão observadas por ym. Se T tiver semântica de valor, será possível alterar xm sem afetar ym. Se T tiver semântica imutável, nem xm nem ym serão alterados. A semântica imutável pode ser simulada por objetos de referência ou valor. Strings são objetos de referência imutáveis.
Supercat
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.