Usando REF & OUT palavras-chave com Passagem por referência e passando por valor em C #

StackOverflow https://stackoverflow.com/questions/1683394

  •  18-09-2019
  •  | 
  •  

Pergunta

Aqui está o que eu entendo até agora:

passagem por valor

Passagem por valor significa uma cópia de um argumento é passado. Alterações a essa cópia não alterar o original.

passar por referência

Passar por referência significa uma referência ao original é passado. alterações à referência afetam o original.

REF palavra-chave

REF diz ao compilador que o objeto é inicializado antes de entrar na função. REF significa que o valor já estiver definido, o método pode, portanto, lê-lo e modificá-lo. REF é duas maneiras, tanto dentro como fora.

OUT palavra-chave

OUT diz ao compilador que o objeto será intialized dentro da função. OUT significa o valor não estiver já definido, e, portanto, deve ser definida antes de chamar retorno. OUT é apenas um caminho, que está fora.

Pergunta

Assim, em que cenários que você combinar o uso do árbitro e fora palavras-chave, com passagem por referência ou passar por valor? Exemplos iria ajudar tremendamente.

Ajuda muito apreciada.

Foi útil?

Solução

Você faria não combinar ref e out em 1 parâmetro. Ambos 'passagem por referência' médio.

Você pode naturalmente combinar parâmetros ref e parâmetros de saída em um método.

A diferença entre ref e out mentiras principalmente em intenção . ref sinais de transporte de dados de 2 vias, uma para fora meio-caminho.

Mas, além intenção, o compilador C # faixas definida-tarefa e que faz a diferença mais perceptível. Ele também impede o uso indevido (leitura) de um parâmetro de saída.

void SetOne(out int x) 
{
  int y = x + 1; // error, 'x' not definitely assigned.
  x = 1;         // mandatory to assign something
}

void AddTwo(ref int x)
{
    x = x + 2;  // OK, x  is known to be assigned
}

void Main()
{
    int foo, bar;

    SetOne(out foo); // OK, foo does not have to be assigned
    AddTwo(ref foo); // OK, foo assigned by SetOne
    AddTwo(ref bar); // error, bar is unassigned
}

Outras dicas

Ajuda muito apreciada

Sua compreensão será melhorado por um uso correto e cuidadoso da linguagem.

Passagem por valor significa uma cópia de um argumento é passado.

Sim, isso é exatamente preciso.

Alterações a essa cópia não alterar o original.

Não exatamente. Começar por distinguir cuidadosamente entre valores e variáveis ??. Considere o seguinte:

class Foo { public int x; }
...
void N() 
{
  Foo blah = new Foo();
  blah.x = 0;
  M(blah);
}
...
void M(Foo foo)
{
  foo.x = 123; // changes blah.x
  foo = null; // does not change blah
}

As variáveis ??aqui são x, blá e foo. x é um campo, blá é um local, foo é um parâmetro formal.

Os valores aqui são nulos, 0, 123, e uma referência a um exemplo de Foo. Esta referência é um valor. É crucial entender esse fato.

Uma cópia do valor do blah é passado, copiando o valor de blah variável em foo variável. O valor de bla é uma referência para a instância de Foo.

M pode alterar o valor da variável x porque M tem uma cópia do valor de blah, que é uma referência a um Foo. Quando M altera o conteúdo do foo como nulo, isso não muda blá; foo contém uma cópia do valor de blah.

Passar por referência significa uma referência ao original é passado.

Escolha o seu texto com cuidado. O que é o "original"?

Este seria melhor indicado como "passando por meio de referência que uma referência ao argumento, que deve ser uma variável, é passado". Uma maneira mais fácil de pensar sobre isso é que "passar por referência faz com que o parâmetro de um alias para a variável passada como argumento".

alterações à referência afetam o original.

Uma vez que o parâmetro e argumento são aliases para o outro, não é que as alterações à referência afetam o original; A referência é o original. Ambos são a mesma variável .

REF diz ao compilador que o objeto é inicializado antes de entrar na função.

"O objeto" não tem sentido. Você quer dizer "a variável".

"ref" não "dizer ao compilador que a variável é inicializada". Em vez disso, "ref" informa o compilador "você, compilador, deve verificar se a variável é inicializada". Isso é um pouco diferente!

REF significa que o valor já está definido,

Não, ref exige que o variável já está definido. Não há tal coisa como "definir um valor".

o método pode, portanto, lê-lo e modificá-lo.

Quando, por "ele" quer dizer "a variável".

REF é duas maneiras, tanto dentro como fora.

correta.

OUT diz ao compilador que o objeto será intialized dentro da função.

Pare de usar "o objeto" para significar "a variável". Você vai entender as coisas muito mais claramente se você parar de confundir coisas completamente diferentes. não As variáveis ??são objetos. As variáveis ??são locais armazenamento alguns dos quais podem conter valores , e alguns desses valores podem ser referências a objetos .

Assim, a informa ao compilador que a variável será inicializada dentro do método, sim, mas isso não é certo. Você está esquecendo-se dos casos em que o método irá lançar uma exceção, ou o método vai entrar em um loop infinito - esses são também os cenários legais.

OUT significa que o valor não estiver definido,

Mais uma vez, pelo "valor", que quer dizer "a variável". Mas isso não é exato. É perfeitamente legal para passar uma variável inicializada como um parâmetro "out". Inútil, mas legal.

e, portanto, deve ser definida antes de chamar retorno.

"retorno" não é chamado; métodos são chamados. Mas sim, o método deve atribuir um valor à variável antes returning normalmente.

OUT é apenas um caminho, que está fora.

direito.

Assim, em quais cenários você combinar o uso do árbitro e fora palavras-chave

Não existem tais cenários.

Você entender a dinâmica da passagem de qualquer maneira. Alguns cenários de parâmetros podem ser:

  • ref int num para um parâmetro in / out. A função pode modificar o valor na mesma.
  • out int num como ref exceto função deve atribuir um valor a ela antes de retornar.

Em geral saída parâmetros são bons para quando uma função deve retornar vários valores, porque uma função somente tem um valor de retorno (embora possa ser composto).

Às vezes, os provedores de acesso a dados, como alguns métodos ADO.NET, parâmetros de saída uso para fornecer informações de volta. Alguns métodos de acesso a dados de bancos de dados armazenados procedimentos imitar que têm in / out parâmetros.

Editar: Uma estipulação para tipos de referência são os parâmetros ref StringBuilder word ou StringBuilder word (em valor) se comportam da mesma - o exterior string é afetado, embora o implementatoin subjacente pode ser ligeiramente diferente porque naquele momento o foco é a referência e não o valor na pilha.

Editar: eu ter corrigido essa resposta para refletir que o 'fora' palavra-chave em C # não faz o que você pode esperar que ele (por exemplo, um parâmetro verdadeiro OUT no sentido de ciência da computação do termo ). Eu originalmente afirmou que 'fora' era passagem por valor OUT, mas foi provado errado.

Você não pode usar 'fora' e 'ref' juntos. Há três convenções de chamada em C # (NET Framework):

  • No keyword = passagem por valor (IN)
  • 'out' Palavra-chave = passar por referência (REF), sem exigência de atribuição definitiva antes chamada
  • 'ref' Palavra-chave = passar por referência (REF) com uma exigência de atribuição definitiva antes chamada

C # tem nenhum verdadeiro OUT ou IN-OUT capacidade parâmetro.

Para ver que os parâmetros de 'fora' em C # não são parâmetros a verdadeira, você pode usar o seguinte código:

  public class Test
  {
    Action _showValue;

    public void Run()
    {
      string local = "Initial";
      _showValue = () => { Console.WriteLine(local.ToString()); };

      Console.WriteLine("Passing by value");
      inMethod(local);

      Console.WriteLine("Passing by reference with 'out' keyword");
      outMethod(out local);

      Console.WriteLine("Passing by reference with 'ref' keyword");
      refMethod(ref local);

    }

    void inMethod(string arg)
    {
      _showValue();
      arg = "IN";
      _showValue();
    }

    void outMethod(out string arg)
    {
      _showValue();
      arg = "OUT";
      _showValue();
    }

    void refMethod(ref string arg)
    {
      _showValue();
      arg = "REF";
      _showValue();
    }
  }

A saída é:

Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF

Como você pode ver, tanto 'out' e 'ref' realmente passar por REF. A única diferença está em como o compilador trata-los para fins de atribuição definidos.

Usando a palavra-chave OUT é útil se você tem um método que você precisa retornar mais de um valor. Por exemplo, olhar para métodos como int.TryParse().

Usando REF é mais para explicitação de objetos. Tendo em mente que qualquer não-primitiva passou para um método é inerentemente passado por referência não há um monte de necessidade para isso em código gerenciado normal. Ao declarar o REF palavra-chave que você está afirmando que o parâmetro provavelmente será modificado no corpo do método e, portanto, o código de chamada deve estar ciente disso (assim, por que você tem que adicionar explicitamente a ref no código de chamada assim.

Se você entender c ++ talvez isso irá ajudá-lo:

void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++

void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++

void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++

void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)

Você passa por algo ref você quer ser lido e escrito por outra função, por isso você deve passar a variável inicializada para ser lido corretamente.

EDIT: exemplo:

void mymethod(ref int a) {
  a++;
}

Você passa como OUT algo que você quer ser escrito por outra função, mas você não precisa para inicializar-lo como ele não será lido pela função, apenas por escrito.

EDIT: exemplo:

void mymethod2(out string a) {
  a="hello";
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top