Pergunta

Eu estou criando uma função que eu preciso para passar um objeto de modo que ele pode ser modificado pela função.Qual é a diferença entre:

public void myFunction(ref MyClass someClass)

e

public void myFunction(out MyClass someClass)

Qual eu devo usar e por quê?

Foi útil?

Solução

ref diz ao compilador que o objeto é inicializado antes de entrar na função, enquanto out diz ao compilador que o objeto será inicializado dentro da função.

Por enquanto ref são dois caminhos, out é apenas fora.

Outras dicas

o ref Modificador significa que:

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

o out Modificador significa que:

  1. O valor não está definido e não pode ser lido pelo método até está combinado.
  2. O método devo Defina -o 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 referência, ele teria uma cópia impressa do memorando.

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

Vou tentar minha mão em uma explicação:

Acho que entendemos como os tipos de valor funcionam, certo? Os tipos de valor são (int, longos, estrutura etc.). Quando você os envia para uma função sem um comando de referência, ele copia o dados. Qualquer coisa que você faça com esses dados na função afeta apenas a cópia, não o original. O comando REF envia os dados reais e quaisquer alterações afetarão os dados fora da função.

OK na parte confusa, tipos de referência:

Vamos criar um tipo de referência:

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

Quando você estiver novo algum objeto, duas partes são criadas:

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

Agora, quando você envia algum objeto em um método sem ref, copia o referência ponteiro, 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 objeto. Se você modificar uma propriedade em algum objeto Usando referência2, 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á -lo para novos dados, ele não afetará a referência1 nem a referência de dados1 para.

(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? o referência real para algum objeto é enviado 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 o mesmo que enviar algum objeto não por Ref, exceto por duas coisas principais:

1) Quando você anular a referência dentro do método, ele nula o fora do método.

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

2) Agora você pode apontar a referência a 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

Ref está em e Fora.

Você deveria usar out em preferência onde quer que seja suficiente para seus requisitos.

Fora:

Em C#, um método pode retornar apenas um valor. Se você gosta de retornar mais de um valor, pode usar a palavra -chave externa. O modificador de saída retorna como referência de volta. 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, duplo etc. Como um argumento para o parâmetro do método, ele é passado pelo 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", ela refletirá na variável real.

  1. Você precisa inicializar a variável antes de chamar a função.
  2. Não é obrigatório atribuir qualquer valor ao parâmetro REF no método. Se você não mudar o valor, qual é a necessidade de marcá -lo como "referência"?

Estendendo o cão, exemplo de 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;
    }

Como você está passando em um tipo de referência (uma classe), não há necessidade de uso ref Porque por padrão apenas um referência Para o objeto real é passado e, portanto, você sempre altera o objeto 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";
}

Enquanto você passa em uma aula, você não precisa usar ref Se você deseja alterar o objeto dentro do seu método.

ref e out Comporte -se da mesma forma, exceto após as diferenças.

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

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

Criei alguns exemplos mínimos de referência e outros para ilustrar o ponto. Não estou cobrindo as melhores práticas, apenas exemplos para entender as diferenças.

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

"Padeiro"

Isso ocorre porque o primeiro muda sua referência de cordas para apontar para "Baker". Alterar a referência é possível porque você a passou através da palavra -chave REF (=> A referência a uma referência a uma string). A segunda chamada recebe uma cópia da referência à string.

String parece algum tipo de especial no começo. 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 tarefa para a mesma variável via

s = "Baker";

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

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

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

O que você espera? O que você receberá ainda é "capaz" porque você acabou de definir a referência em S como outra instância, enquanto o 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 um nos documentos, mas não será preenchido :-)). Todos os métodos de manipulação de string retornam uma nova instância de string! (É por isso que você geralmente obtém um desempenho melhor ao usar a classe Stringbuilder)

Ref significa que o valor no parâmetro REF já está definido, o método pode ler e modificá -lo. O uso da palavra -chave REF é o mesmo que dizer que o chamador é responsável por inicializar o valor do parâmetro.


Fora Diz ao compilador que a inicialização do objeto é de responsabilidade da função, a função deve atribuir ao parâmetro out. Não tem permissão para deixá -lo não atribuído.

Fora: Uma instrução Return 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 para fora do método e não para ele.

O exemplo a seguir ilustra o seguinte:

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 os 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 de 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 o seguinte:

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 trabalho de saída, como passar por referências e passar por ponteiros como em C ++.

Para a referência, o argumento deve declarar e inicializar.

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

Tempo De Criação:

(1) Vamos criar o método de chamada Main()

(2) ele cria um objeto de Lista (que é um tipo de referência de objeto) e 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) tempo de execução atribui uma memória na pilha #00, grande o suficiente para armazenar um endereço (#00 = myList, já que os nomes de variáveis são realmente apenas aliases para localizações de memória)

(4) o tempo de execução cria uma lista de objeto no heap na localização de memória #FF( todos estes endereços são, por exemplo, amor)

(5) tempo de execução deve, em seguida, armazenar o endereço inicial #FF do objeto em #00(ou em palavras, armazena a referência do objeto da Lista em que o ponteiro myList)

De volta ao Tempo de Criação:

(6) Nós, em seguida, passar a Lista de objeto como argumento myParamList para a chamada de método modifyMyList e atribuir um novo objeto de Lista para 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) tempo de execução inicia a rotina de chamada para o método chamado e, como parte dele, verifica o tipo de parâmetros.

(8) Após encontrar o tipo de referência, atribui uma memória na pilha #04 para a variável de parâmetro aliasing myParamList.

(9) em seguida, armazena o valor de #FF-lo bem.

(10) o tempo de execução cria uma lista de objeto no heap na localização de memória #004 e substitui #FF #04 com este valor(ou cancelou a referência a Lista original de objeto e apontou para o novo objecto de Lista deste método)

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


O ref palavra-chave é uma directiva 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 de método.Ele vai usar o original #00 ponteiro para operar sobre o objeto em #FF.Se o ponteiro não inicializado, o tempo de execução irá parar de reclamar que ele não pode prosseguir, pois a variável não inicializada

O fora palavra-chave é uma directiva de compilador, que praticamente é o mesmo como ref, com uma ligeira modificação em (9) e (10).O compilador espera o argumento a ser uninitialised e vai continuar com a (8), (4) e (5) para criar um objeto no heap e armazena o endereço inicial no argumento variável.Não uninitialised de erro será gerada e qualquer referência anterior armazenados serão perdidos.

Eles são praticamente iguais - a única diferença é que uma variável que você passa como um parâmetro out não precisa ser inicializado e o método usando o parâmetro REF precisa defini -lo como algo.

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

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

Abaixo eu mostrei um exemplo usando os dois Ref e Fora. Agora, todos vocês serão liberados sobre a referência e fora.

No exemplo abaixo mencionado quando eu comento // myRefobj = new MyClass {name = "Ref Out E Out Calling !!"};linha, receberá um erro dizendo "Uso de variável local não atribuída 'MyRefobj'", mas não há esse erro em Fora.

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

Onde usar: Quando estamos chamando um procedimento sem o 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; }
} 
 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 este código, ele descreverá sua diferença completa quando você usa "ref", significa que você já inicializa essa string int/

Mas quando você usa "Out", funciona em ambas

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

Out:O limite de palavras-chave também é usado para passar um argumento como ref palavra-chave, mas o argumento pode ser passado sem atribuir qualquer valor para ele.Um argumento que é transmitido usando uma palavra-chave deve ser inicializado no método chamado antes, ele volta para o 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 reduzir a sobrecarga de método

Tanto ref e out não pode ser usado em sobrecarga de método, simultaneamente.No entanto, ref e são tratados de forma diferente em tempo de execução, mas eles são tratados, mesmo em tempo de compilação (CLR não diferencia entre os dois, enquanto ele criou IL ref e fora).

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

Do ponto de vista do chamador, C# irá em muitas circunstâncias assumirá ao chamar um método com um out O parâmetro fará com que a variável passada seja escrita 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 myDictionary identifica um IDictionary<TKey,TValue> implementação escrita em um idioma que não seja C#, embora MyStruct s = new MyStruct(myDictionary); Parece uma tarefa, poderia potencialmente sair s não modificado.

Observe que os construtores escritos em vb.net, ao contrário dos em C#, não fazem suposições sobre se os métodos chamados modificarão qualquer out parâmetros e limpe todos os campos incondicionalmente. O comportamento ímpar aludido 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 no vb.net.

Se você deseja passar no seu parâmetro como um referência, você deve inicializá -lo antes de passar o parâmetro para o próprio Compilador da função, ele mostrará o erro. No caso do parâmetro out, você não precisa inicializar o parâmetro do objeto antes de passá -lo para o Método. Você pode inicializar o objeto no próprio método de chamada.

Bem como permitindo que você reatribuir de alguém variável para outra instância de uma classe, retornar vários valores, etc, usando ref ou out permite que alguém saiba o que você precisa e o que você pretende fazer com a variável eles fornecem

  • Você não precisa ref ou out se todos você está indo fazer é modificar as coisas dentro o MyClass instância que é passado no argumento someClass.

    • O método de chamada vai ver as mudanças como someClass.Message = "Hello World" se você usar ref, out ou nada
    • Escrever someClass = new MyClass() dentro myFunction(someClass) swaps de fora o objeto visto pelo someClass no âmbito da myFunction método só.O método de chamada ainda não conhece o original MyClass instância é criada e passada para o método
  • Você necessidade ref ou out se você está pensando em trocar o someClass para um novo objeto e quer o método de chamada para ver a alteração

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

Outros programadores existe

E querem saber o que você vai fazer com seus dados.Imagine que você está escrevendo uma biblioteca que será usada por milhões de desenvolvedores.Você quer saber o que você vai fazer com suas variáveis quando eles chamada de seus métodos

  • Usando ref faz uma declaração de "Passar uma variável atribuído algum valor quando você chamar o meu método.Esteja ciente de que eu possa alterá-lo para algo completamente diferente durante o curso do meu método.Não é de esperar que a variável apontar para o objeto antigo, quando eu sou feito"

  • Usando out faz uma declaração de "Passar uma variável de espaço reservado para o meu método.Não importa se ele tem um valor ou não;o compilador vai me forçar para atribuir um novo valor.Eu absolutamente garantia de que o objeto apontado pela variável antes de você chamou o meu método, vai ser diferente até o momento que eu sou feito

A propósito, em C#7.2 há um in modificador muito

E que impede que o método de trocar o passado em exemplo para uma instância diferente.Pensar é como dizer para os milhões de desenvolvedores "passe-me o original da variável de referência, e eu prometo não troque seus cuidadosamente trabalhada dados para outra coisa". in tem algumas peculiaridades, e, em alguns casos, tais como quando uma conversão implícita pode ser necessário para tornar a sua curta compatível com um in int o compilador irá tornar temporariamente um int, alargar a sua curta para ele, passar por referência, e de terminar.Ele pode fazer isso porque você já declarou que não vamos mexer com ele.


A Microsoft fez isso com a .TryParse métodos numéricos tipos:

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

Ao declarar o parâmetro como out eles estão ativamente declarar aqui "somos definitivamente vai mudar o seu meticulosamente trabalhada valor de 98234957 para algo mais"

Claro, eles meio que preciso, para coisas como a análise de tipos de valor porque, se o método de análise não era permitida a troca do tipo de valor por algo que não iria funcionar muito bem..Mas imagine houve alguns fictícios método em alguma biblioteca que você está criando:

public void PoorlyNamedMethod(out SomeClass x)

Você pode ver que é um out, e assim, você pode saber que, se você passar horas a analisar números, criando o perfeito SomeClass:

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

Bem, isso foi um desperdício de tempo, de tomar todas aquelas horas para fazer com que perfeito classe.Isso definitivamente vai ser jogados fora e substituídos por PoorlyNamedMethod

Lembre -se bem de que o parâmetro de referência que é passado dentro da função é 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;
    }

Isso escreverá cachorro, não gato. Portanto, você deve trabalhar diretamente em algum objeto.

Posso não ser tão bom nisso, mas certamente as cordas (mesmo sendo tecnicamente tipos de referência e vivem na pilha) são passadas por valor, não de 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 referência, se deseja que as alterações existam fora do escopo da função que os faz, não está passando uma referência de outra forma.

Tanto quanto estou ciente, você só precisa de ref para estruturas/tipos de valor e a própria string, pois a 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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top