Quando passo a string
para uma função, é passado um ponteiro para o conteúdo da sequência ou toda a sequência é passada para a função na pilha como struct
seria?
Quando passo a string
para uma função, é passado um ponteiro para o conteúdo da sequência ou toda a sequência é passada para a função na pilha como struct
seria?
Respostas:
Uma referência é passada; no entanto, não é tecnicamente passado por referência. Esta é uma distinção sutil, mas muito importante. Considere o seguinte código:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Há três coisas que você precisa saber para entender o que acontece aqui:
strMain
não são passadas por referência. É um tipo de referência, mas a própria referência é passada por valor . Sempre que você passa um parâmetro sem a ref
palavra - chave (sem contar os out
parâmetros), passa algo por valor.Então isso deve significar que você está ... passando uma referência por valor. Como é um tipo de referência, apenas a referência foi copiada na pilha. Mas o que isso significa?
Variáveis C # são tipos de referência ou tipos de valor . Os parâmetros C # são passados por referência ou passados por valor . A terminologia é um problema aqui; estes parecem a mesma coisa, mas não são.
Se você passa um parâmetro de QUALQUER tipo e não usa a ref
palavra - chave, passa-o por valor. Se você passou por valor, o que realmente passou foi uma cópia. Mas se o parâmetro era um tipo de referência, o que você copiava era a referência, não o que estava apontando.
Aqui está a primeira linha do Main
método:
string strMain = "main";
Criamos duas coisas nessa linha: uma string com o valor main
armazenado na memória em algum lugar e uma variável de referência chamada strMain
apontando para ela.
DoSomething(strMain);
Agora passamos essa referência para DoSomething
. Nós passamos por valor, o que significa que fizemos uma cópia. É um tipo de referência, o que significa que copiamos a referência, não a string em si. Agora, temos duas referências que apontam para o mesmo valor na memória.
Aqui está o topo do DoSomething
método:
void DoSomething(string strLocal)
Nenhuma ref
palavra-chave, portanto, strLocal
e strMain
são duas referências diferentes apontando para o mesmo valor. Se reatribuirmos strLocal
...
strLocal = "local";
... não alteramos o valor armazenado; pegamos a referência chamada strLocal
e apontamos para uma nova string. O que acontece strMain
quando fazemos isso? Nada. Ainda está apontando para a corda antiga.
string strMain = "main"; // Store a string, create a reference to it
DoSomething(strMain); // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main"
Vamos mudar o cenário por um segundo. Imagine que não estamos trabalhando com strings, mas com algum tipo de referência mutável, como uma classe que você criou.
class MutableThing
{
public int ChangeMe { get; set; }
}
Se você seguir a referência objLocal
ao objeto para o qual ele aponta, poderá alterar suas propriedades:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Ainda existe apenas uma MutableThing
na memória, e a referência copiada e a referência original ainda apontam para ela. As propriedades do MutableThing
próprio foram alteradas :
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain
DoSomething(objMain); // now it's 0 on objLocal
Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain
}
Ah, mas as cordas são imutáveis! Não há ChangeMe
propriedade para definir. Você não pode fazer strLocal[3] = 'H'
em C # como faria com uma char
matriz de estilo C ; você precisa construir uma sequência totalmente nova. A única maneira de mudar strLocal
é apontar a referência para outra string, e isso significa que nada que você faça strLocal
pode afetarstrMain
. O valor é imutável e a referência é uma cópia.
Para provar que há uma diferença, eis o que acontece quando você passa uma referência por referência:
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Desta vez, a string Main
realmente muda porque você passou a referência sem copiá-la na pilha.
Portanto, mesmo que as strings sejam tipos de referência, transmiti-las por valor significa que tudo o que acontece no receptor não afetará a string no chamador. Mas, como são tipos de referência, você não precisa copiar toda a cadeia de caracteres da memória quando quiser distribuí-la.
ref
palavra - chave. Para provar que passar por referência faz a diferença, veja esta demonstração: rextester.com/WKBG5978
ref
palavra-chave tem utilidade. Estava apenas tentando explicar por que alguém poderia pensar em passar um tipo de referência por valor em C # parece a noção "tradicional" (isto é, C) de passar por referência (e passar um tipo de referência por referência em C # parece mais passar uma referência a uma referência por valor).
Foo(string bar)
poderia ser pensado como Foo(char* bar)
passo que Foo(ref string bar)
seria Foo(char** bar)
(ou Foo(char*& bar)
ou Foo(string& bar)
em C ++). Claro, não é como você deve pensar nisso todos os dias, mas na verdade me ajudou a finalmente entender o que está acontecendo sob o capô.
Strings em C # são objetos de referência imutáveis. Isso significa que as referências a eles são passadas (por valor) e, depois que uma string é criada, você não pode modificá-la. Os métodos que produzem versões modificadas da sequência (substrings, versões aparadas etc.) criam cópias modificadas da sequência original.
Strings são casos especiais. Cada instância é imutável. Quando você altera o valor de uma sequência, está alocando uma nova sequência na memória.
Portanto, apenas a referência é passada para sua função, mas quando a string é editada, ela se torna uma nova instância e não modifica a instância antiga.
Uri
(class) e Guid
(struct) também são casos especiais. Não vejo como System.String
age como um "tipo de valor", mais do que outros tipos imutáveis ... de origem de classe ou estrutura.
Uri
& Guid
- você pode apenas atribuir um valor literal de string a uma variável de string. A string parece ser mutável, como um int
ser reatribuído, mas está criando um objeto implicitamente - sem new
palavra-chave.