Dê uma olhada no seguinte programa:
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList(myList);
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList(List<int> myList)
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
Eu presumi myList
que teria passado ref
, e a saída
3
4
A lista é de fato "passada por ref", mas apenas a sort
função tem efeito. A seguinte declaração myList = myList2;
não tem efeito.
Portanto, a saída é de fato:
10
50
100
Você pode me ajudar a explicar esse comportamento? Se, de fato, myList
não for ignorado (como parece por myList = myList2
não ter efeito), como o myList.Sort()
efeito tem efeito?
Eu estava presumindo que até mesmo essa declaração não teria efeito e a saída seria:
100
50
10
c#
list
pass-by-reference
nmdr
fonte
fonte
ChangeList
retornar um emList<int>
vez de ser umvoid
se de fato estiver criando uma nova lista.Respostas:
Você está passando uma referência para a lista , mas não está passando a variável da lista por referência - então, quando você chama
ChangeList
o valor da variável (ou seja, a referência - pense no "ponteiro") é copiada - e muda para o valor do parâmetro dentroChangeList
não é visto porTestMethod
.experimentar:
Isso então passa uma referência à variável local
myRef
(conforme declarado emTestMethod
); agora, se você reatribuir o parâmetro interno,ChangeList
também estará reatribuindo a variável internaTestMethod
.fonte
ChangeList
, apenas a referência é copiada - é o mesmo objeto. Se você mudar o objeto de alguma forma, tudo que tiver uma referência a esse objeto verá a mudança.Inicialmente, pode ser representado graficamente da seguinte forma:
Então, a classificação é aplicada
myList.Sort();
Finalmente, quando você fez
myList' = myList2
:, você perdeu aquele da referência, mas não o original e a coleção ficou classificada.Se você usar por referência (
ref
), entãomyList'
emyList
se tornará o mesmo (apenas uma referência).Nota: Eu uso
myList'
para representar o parâmetro que você usa emChangeList
(porque você deu o mesmo nome do original)fonte
Aqui está uma maneira fácil de entender isso
Sua lista é um objeto criado no heap. A variável
myList
é uma referência a esse objeto.Em C # você nunca passa objetos, você passa suas referências por valor.
Quando você acessa o objeto de lista por meio da referência passada
ChangeList
(durante a classificação, por exemplo), a lista original é alterada.A atribuição no
ChangeList
método é feita para o valor da referência, portanto, nenhuma alteração é feita na lista original (ainda na pilha, mas não mais referenciada na variável do método).fonte
Este link o ajudará a entender a passagem por referência em C #. Basicamente, quando um objeto do tipo de referência é passado por valor para um método, apenas os métodos que estão disponíveis naquele objeto podem modificar o conteúdo do objeto.
Por exemplo, o método List.sort () altera o conteúdo da lista, mas se você atribuir algum outro objeto à mesma variável, essa atribuição será local para aquele método. É por isso que myList permanece inalterado.
Se passarmos o objeto do tipo de referência usando a palavra-chave ref, podemos atribuir algum outro objeto à mesma variável e isso muda o próprio objeto inteiro.
(Editar: esta é a versão atualizada da documentação vinculada acima.)
fonte
C # apenas faz uma cópia superficial quando passa por valor, a menos que o objeto em questão seja executado
ICloneable
(o que aparentemente aList
classe não executa).Isso significa que ele copia a
List
si mesmo, mas as referências aos objetos dentro da lista permanecem as mesmas; ou seja, os ponteiros continuam a fazer referência aos mesmos objetos do originalList
.Se você alterar os valores das coisas que suas novas
List
referências fazem, você também altera o originalList
(uma vez que está referenciando os mesmos objetos). No entanto, você então altera quaismyList
referências inteiramente, para um novoList
, e agora apenas o originalList
está fazendo referência a esses inteiros.Leia a passagem de Referência-Type Parâmetros seção de este artigo MSDN em "Passando parâmetros" para mais informações.
"How do I Clone a Generic List in C #" do StackOverflow fala sobre como fazer uma cópia profunda de uma lista.
fonte
Embora eu concorde com o que todos disseram acima. Eu tenho uma opinião diferente sobre este código. Basicamente, você está atribuindo a nova lista à variável local myList, não à global. se você alterar a assinatura de ChangeList (List myList) para private void ChangeList (), verá a saída de 3, 4.
Aqui está meu raciocínio ... Mesmo que a lista seja passada por referência, pense nisso como uma variável de ponteiro por valor. Quando você chama ChangeList (myList), você está passando o ponteiro para (Global) myList. Agora, isso é armazenado na variável myList (local). Portanto, agora sua (local) myList e (global) myList estão apontando para a mesma lista. Agora você faz uma classificação => funciona porque (local) myList está referenciando a myList original (global) Em seguida, você cria uma nova lista e atribui o ponteiro a essa myList (local). Mas, assim que a função sai, a variável myList (local) é destruída. HTH
fonte
Use a
ref
palavra - chave.Veja a referência definitiva aqui para entender a passagem de parâmetros.
Para ser específico, veja isso para entender o comportamento do código.
EDIT:
Sort
funciona na mesma referência (que é passada por valor) e, portanto, os valores são ordenados. No entanto, atribuir uma nova instância ao parâmetro não funcionará porque o parâmetro é passado por valor, a menos que você coloqueref
.Colocar
ref
permite que você altere o ponteiro para a referência a uma nova instância deList
no seu caso. Semref
, você pode trabalhar no parâmetro existente, mas não pode fazer com que aponte para outra coisa.fonte
Existem duas partes de memória alocadas para um objeto do tipo de referência. Um na pilha e um na pilha. A parte na pilha (também conhecida como ponteiro) contém referência à parte na pilha - onde os valores reais são armazenados.
Quando a palavra-chave ref não é usada, apenas uma cópia de parte na pilha é criada e passada para o método - referência à mesma parte na pilha. Portanto, se você alterar algo na parte do heap, essa alteração permanecerá. Se você alterar o ponteiro copiado - atribuindo-o para se referir a outro local na pilha - não afetará o ponteiro de origem fora do método.
fonte