O que ref, val e out significam nos parâmetros do método?
Pergunta
Procuro uma resposta clara, concisa e precisa.
Idealmente, como a resposta real, embora sejam bem-vindos links para boas explicações.
Isso também se aplica ao VB.Net, mas as palavras-chave são diferentes - ByRef
e ByVal
.
Solução
Por padrão (em C#), passar um objeto para uma função, na verdade, passa uma cópia da referência para esse objeto.Alterar o parâmetro em si altera apenas o valor do parâmetro, e não a variável que foi especificada.
void Test1(string param)
{
param = "new value";
}
string s1 = "initial value";
Test1(s1);
// s1 == "initial value"
Usando out
ou ref
passa uma referência à variável especificada na chamada da função.Qualquer alteração no valor de um out
ou ref
parâmetro será passado de volta ao chamador.
Ambos out
e ref
comportar-se de forma idêntica, exceto por uma pequena diferença: ref
parâmetros devem ser inicializados antes da chamada, enquanto out
parâmetros podem ser não inicializados.Por extensão, ref
os parâmetros são garantidos para serem inicializados no início do método, enquanto out
parâmetros são tratados como não inicializados.
void Test2(ref string param)
{
param = "new value";
}
void Test3(out string param)
{
// Use of param here will not compile
param = "another value";
}
string s2 = "initial value";
string s3;
Test2(ref s2);
// s2 == "new value"
// Test2(ref s3); // Passing ref s3 will not compile
Test3(out s2);
// s2 == "another value"
Test3(out s3);
// s3 == "another value"
Editar:Como DP aponta, a diferença entre out
e ref
é aplicado apenas pelo compilador C#, não pelo CLR.Até onde eu sei, VB não tem equivalente para out
e implementos ref
(como ByRef
) apenas, correspondendo ao suporte do CLR.
Outras dicas
Uma observação adicional sobre ref vs.fora:A distinção entre os dois é imposta pelo compilador C#.O CLR não faz distinção entre out e ref.Isso significa que você não pode ter dois métodos cujas assinaturas diferem apenas por um out ou ref
void foo(int value) {}
// Only one of the following would be allowed
// valid to overload with ref
void foo(ref int value) {}
// OR with out
void foo(out int value) {}
out
significa que o parâmetro será inicializado pelo método:
int result; //not initialised
if( int.TryParse( "123", out result ) )
//result is now 123
else
//if TryParse failed result has still be
// initialised to its default value (0)
ref
forçará a referência subjacente a ser passada:
void ChangeMyClass1( MyClass input ) {
input.MyProperty = "changed by 1";
input = null;
//can't see input anymore ...
// I've only nulled my local scope's reference
}
void ChangeMyClass2( ref MyClass input ) {
input.MyProperty = "changed by 2";
input = null;
//the passed reference is now null too.
}
MyClass tester = new MyClass { MyProperty = "initial value" };
ChangeMyClass1( tester );
// now tester.MyProperty is "changed by 1"
ChangeMyClass2( ref tester );
// now tester is null
Uma das minhas perguntas no stackoverflow também aborda esse tópico.
Ele lida com cerca de "passagem por referência" e "passagem por valor" em diferentes tipos de idiomas, c# está incluído então talvez você também possa encontrar algumas informações extras lá.
Basicamente tudo se resume a:
- referência:o parâmetro com a palavra-chave ref será passado por referência
- fora:o parâmetro com a palavra-chave out será tratado como um parâmetro de saída
mas essa é realmente a resposta mais básica que você pode dar, pois é um pouco mais complexa do que é declarada aqui