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ê?
MyClass
seria umclass
tipo, ou seja, um tipo de referência. Nesse caso, o objeto que você passa pode ser modificado pelomyFunction
mesmo semref
/out
palavra-chave.myFunction
receberá uma nova referência que aponta para o mesmo objeto e pode modificar o mesmo objeto o quanto desejar. A diferença que aref
palavra - chave faria seriamyFunction
receber a mesma referência ao mesmo objeto. Isso seria importante apenas semyFunction
mudássemos a referência para apontar para outro objeto.Respostas:
ref
informa ao compilador que o objeto foi inicializado antes de inserir a função, enquantoout
informa ao compilador que o objeto será inicializado dentro da função.Então, enquanto
ref
é bidirecional,out
é somente fora.fonte
O
ref
modificador significa que:O
out
modificador significa que:fonte
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?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.
fonte
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:
Quando você cria um novo objeto , duas partes são criadas:
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:
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.
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.
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:
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.
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.
fonte
ref
eout
parâmetros.out
palavra-chave?ref está dentro e fora .
Você deve usar
out
de preferência onde for suficiente para seus requisitos.fonte
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.
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.
fonte
Estendendo o cão, exemplo do gato. O segundo método com ref altera o objeto referenciado pelo chamador. Daí "gato" !!!
fonte
Como você está passando um tipo de referência (uma classe), não há necessidade de usá-lo
ref
porque, por padrão, apenas uma referência ao objeto real é passada e, portanto, você sempre altera o objeto atrás da referência.Exemplo:
Desde que você passe em uma classe, não precisará usá-
ref
lo se quiser alterar o objeto dentro do seu método.fonte
someObject = null
toBar
end execute. Seu código funcionará bem, pois apenasBar
a referência à instância foi anulada. Agora mudeBar
paraBar(ref MyClass someObject)
e executar novamente - você vai ter umaNullReferenceException
causaFoo
de referência 's para a instância foi anulada também.ref
eout
se comportam de maneira semelhante, exceto as seguintes diferenças.ref
A variável deve ser inicializada antes do uso.out
variável pode ser usada sem atribuiçãoout
O parâmetro deve ser tratado como um valor não atribuído pela função que o utiliza. Portanto, podemos usar oout
parâmetro inicializado no código de chamada, mas o valor será perdido quando a função for executada.fonte
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. Não estou abordando as melhores práticas, apenas exemplos para entender as diferenças.
https://gist.github.com/2upmedia/6d98a57b68d849ee7091
fonte
"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
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
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:
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)
fonte
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.
fonte
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:
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:
fonte
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
fonte
out double Half_nbr
.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
.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
myParamList
ao método chamadomodifyMyList
e atribuímos um novo objeto List a ele.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
myList
ponteiro 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.
fonte
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
ref
ouout
permite que outra pessoa saiba o que você precisa dela e o que você pretende fazer com a variável que ela forneceVocê não precisa
ref
ouout
se tudo o que você fará é modificar as coisas dentro daMyClass
instância que é passada no argumentosomeClass
.someClass.Message = "Hello World"
se você usaref
,out
ou nadasomeClass = new MyClass()
internamyFunction(someClass)
troca o objeto visto apenassomeClass
no escopo domyFunction
método. O método de chamada ainda conhece aMyClass
instância original criada e transmitida ao seu métodoVocê precisa
ref
ouout
planeja trocar asomeClass
saída por um objeto totalmente novo e deseja que o método de chamada veja sua alteraçãosomeClass = new MyClass()
dentromyFunction(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
ref
faz 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
out
faz 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 terminarA propósito, no C # 7.2 também há um
in
modificadorE 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".
in
possui algumas peculiaridades e, em alguns casos, como onde uma conversão implícita pode ser necessária para tornar seu curta compatível comin int
o 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
.TryParse
métodos nos tipos numéricos:Ao sinalizar o parâmetro como
out
eles 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:
Você pode ver que é um
out
e, portanto, pode saber que, se você passar horas processando números, criando o SomeClass perfeito: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
fonte
Para quem procura uma resposta concisa.
fonte
Para ilustrar as excelentes explicações, desenvolvi o seguinte aplicativo de console:
AppendWorld
: Uma cópia doStringList
nomeLiStri
é 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, fazLiStri
referência a outroList<string>
objeto dentro do método que não afeta a lista original.HalloWelt
:LiStriRef
é um alias do já inicializadoListStringRef
. OList<string>
objeto passado é usado para inicializar um novo, portanto,ref
era necessário.CiaoMondo
:LiStriOut
é um aliasListStringOut
e 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
out
e você não deve usá-ref
lo 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, useref
para um objeto já inicializado eout
para métodos que devem inicializar um novo objeto para o argumento passado. Além disso,ref
eout
se comporta da mesma maneira.fonte
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.
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.
fonte
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
fonte
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
fonte
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.
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).
fonte
Do ponto de vista de um método que recebe um parâmetro, a diferença entre
ref
eout
é que o C # exige que os métodos gravem em todos osout
parâmetros antes de retornar e não devem fazer nada com esse parâmetro, além de passá-lo comoout
parâmetro ou gravá-lo , até que tenha sido passado comoout
parâ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 umout
parâ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
out
parâ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:Se
myDictionary
identificar umaIDictionary<TKey,TValue>
implementação escrita em um idioma diferente de C #, mesmo queMyStruct s = new MyStruct(myDictionary);
pareça uma atribuição, ela poderá não sers
modificada.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
out
parâ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.fonte
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.
fonte
Lembre-se bem de que o parâmetro de referência que é passado dentro da função é diretamente trabalhado.
Por exemplo,
Isso escreverá Dog, não Cat. Portanto, você deve trabalhar diretamente em someObject.
fonte
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?
É 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.
fonte
Capitalize()
que altere seu conteúdo para maiúsculas. Se você substituísse sua linhaa = "testing";
pora.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.