Bir string
işleve a ilettiğimde, dizenin içeriğine bir işaretçi iletilir mi, yoksa dizenin tamamı işleve bir yığın üzerinde işleve iletilir struct
mi?
Bir string
işleve a ilettiğimde, dizenin içeriğine bir işaretçi iletilir mi, yoksa dizenin tamamı işleve bir yığın üzerinde işleve iletilir struct
mi?
Yanıtlar:
Bir referans aktarılır; ancak teknik olarak referans yoluyla geçilmez. Bu ince ama çok önemli bir ayrımdır. Aşağıdaki kodu göz önünde bulundurun:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Burada ne olduğunu anlamak için bilmeniz gereken üç şey var:
strMain
aktarılmaz. Bu bir başvuru türüdür, ancak başvurunun kendisi değere göre aktarılır . ref
Anahtar kelime olmadan bir parametreyi out
her ilettiğinizde ( parametreleri saymadan ), bir şeyi değere göre geçirmiş olursunuz.Bu demek oluyor ki ... değere göre bir referans geçiyorsun. Bir referans türü olduğundan, yalnızca referans yığına kopyalandı. Ama bu ne anlama geliyor?
C # değişkenleri başvuru türleri veya değer türleridir . C # parametreleri ya başvuruya göre ya da değere göre geçirilir . Terminoloji burada bir sorundur; bunlar aynı şey gibi geliyor ama değiller.
HERHANGİ türde bir parametre geçirirseniz ve ref
anahtar kelimeyi kullanmazsanız , o zaman onu değere göre geçirmişsinizdir. Değerine göre geçtiyseniz, gerçekten geçtiğiniz şey bir kopyadır. Ancak parametre bir referans tipiyse, kopyaladığınız şey referanstır, işaret ettiği şey değil.
İşte Main
yöntemin ilk satırı :
string strMain = "main";
Bu satırda iki şey yarattık: bir main
yerde hafızada saklanan değere sahip bir dize ve ona strMain
işaret eden bir referans değişkeni .
DoSomething(strMain);
Şimdi bu referansı aktarıyoruz DoSomething
. Değerine göre geçtik, yani bir kopya çıkardık. Bu bir referans türüdür, yani dizeyi değil referansı kopyaladık. Şimdi bellekte her biri aynı değeri gösteren iki referansımız var.
İşte DoSomething
yöntemin en tepesi :
void DoSomething(string strLocal)
Hiçbir ref
anahtar kelime, bu yüzden strLocal
ve strMain
aynı değerde işaret iki farklı referanslar vardır. Yeniden atarsak strLocal
...
strLocal = "local";
... saklanan değeri değiştirmedik; denilen referansı aldık strLocal
ve yepyeni bir dizgiye hedefledik. Bunu strMain
yaptığımızda ne olur ? Hiçbir şey değil. Hala eski dizgiye işaret ediyor.
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"
Senaryoyu bir saniyeliğine değiştirelim. Dizelerle çalışmadığımızı, ancak oluşturduğunuz sınıf gibi değiştirilebilir bir referans türüyle çalıştığımızı hayal edin.
class MutableThing
{
public int ChangeMe { get; set; }
}
Gösterdiği nesnenin referansını takip ederseniz objLocal
, özelliklerini değiştirebilirsiniz:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
MutableThing
Hafızada hala sadece bir tane var ve hem kopyalanan referans hem de orijinal referans hala onu gösteriyor. Kendisinin özellikleri MutableThing
değişti :
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, ama dizeler değişmez! ChangeMe
Ayarlanacak bir özellik yok . Sen yapamazsın strLocal[3] = 'H'
sen C tarzı ile olabilir gibi C # char
dizide; bunun yerine tamamen yeni bir dizi oluşturmanız gerekir. Değiştirmenin tek yolu strLocal
referansı başka bir dizeye yönlendirmektir ve bu, yapacağınız hiçbir şeyin strLocal
etkileyemeyeceği anlamına gelir strMain
. Değer sabittir ve referans bir kopyadır.
Bir fark olduğunu kanıtlamak için, referansla bir referansı ilettiğinizde şunlar olur :
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Bu sefer, dizge Main
gerçekten değişiyor çünkü referansı yığına kopyalamadan geçtiniz.
Dolayısıyla dizeler referans türler olsa bile, onları değere göre geçirmek, aranan uçta olup bitenlerin çağıran dizgeyi etkilemeyeceği anlamına gelir. Ancak referans türler oldukları için , dolaştırmak istediğinizde tüm dizeyi bellekte kopyalamanıza gerek yoktur.
ref
. Referansla geçişin
ref
anahtar kelime yarar vardır, ben sadece bir nedenini anlatmaya çalışıyordu olabilir C # değeri tarafından bir referans türü geçen düşünmek referans olarak geçen (ve bir referans türü geçme "geleneksel" (yani C) kavramı gibi görünüyor C # 'da referans olarak, değere göre bir referansa bir referans iletmek gibi görünür).
Foo(string bar)
olarak düşünülebilir olabilir Foo(char* bar)
oysa Foo(ref string bar)
olacağını Foo(char** bar)
(ya da Foo(char*& bar)
ya da Foo(string& bar)
C ++). Elbette, bunu her gün nasıl düşünmeniz gerektiği değil, ama nihayet kaputun altında neler olduğunu anlamama yardımcı oldu.
C # 'teki dizeler değişmez referans nesneleridir. Bu, onlara yapılan referansların (değere göre) aktarıldığı ve bir dize oluşturulduktan sonra onu değiştiremeyeceğiniz anlamına gelir. Dizenin değiştirilmiş sürümlerini (alt dizeler, kırpılmış sürümler, vb.) Üreten yöntemler, orijinal dizenin değiştirilmiş kopyalarını oluşturur .
Dizeler özel durumlardır. Her örnek değişmezdir. Bir dizenin değerini değiştirdiğinizde, belleğe yeni bir dizge ayırmış olursunuz.
Dolayısıyla, işlevinize yalnızca referans iletilir, ancak dize düzenlendiğinde yeni bir örnek olur ve eski örneği değiştirmez.
Uri
(sınıf) ve Guid
(yapı) da özel durumlardır. System.String
Bir "değer türü" gibi davrandığını, sınıf veya yapı kökenli diğer değişmez türlerden daha fazla görmüyorum .
Uri
&'nin aksine Guid
- bir dize değişkenine bir dize-değişmez değer atayabilirsiniz. Dize, int
yeniden atanmış gibi değiştirilebilir gibi görünür , ancak örtük olarak bir nesne yaratır - new
anahtar kelime olmadan .