Ketika saya meneruskan string
ke suatu fungsi, apakah pointer ke konten string dilewatkan, atau seluruh string diteruskan ke fungsi di stack seperti a struct
?
Ketika saya meneruskan string
ke suatu fungsi, apakah pointer ke konten string dilewatkan, atau seluruh string diteruskan ke fungsi di stack seperti a struct
?
Jawaban:
Sebuah referensi dilewatkan; Namun, secara teknis itu tidak diteruskan oleh referensi. Ini adalah perbedaan yang halus, tetapi sangat penting. Perhatikan kode berikut:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Ada tiga hal yang perlu Anda ketahui untuk memahami apa yang terjadi di sini:
strMain
tidak diteruskan oleh referensi. Ini adalah tipe referensi, tetapi referensi itu sendiri diteruskan oleh nilai . Setiap kali Anda meneruskan parameter tanpa ref
kata kunci (tidak menghitung out
parameter), Anda telah melewati sesuatu berdasarkan nilai.Jadi itu berarti Anda ... memberikan referensi berdasarkan nilai. Karena ini adalah tipe referensi, hanya referensi yang disalin ke tumpukan. Tapi apa maksudnya itu?
Variabel C # adalah tipe referensi atau tipe nilai . Parameter C # bisa diteruskan dengan referensi atau diteruskan dengan nilai . Terminologi adalah masalah di sini; ini terdengar seperti hal yang sama, tetapi sebenarnya tidak.
Jika Anda meneruskan parameter jenis APA PUN, dan Anda tidak menggunakan ref
kata kunci, Anda telah meneruskannya menurut nilai. Jika Anda lulus berdasarkan nilai, yang sebenarnya Anda berikan adalah salinannya. Tetapi jika parameternya adalah tipe referensi, maka hal yang Anda salin adalah referensi tersebut, bukan apa pun yang ditunjukkannya.
Inilah baris pertama dari Main
metode ini:
string strMain = "main";
Kami telah membuat dua hal di baris ini: string dengan nilai yang main
disimpan di memori di suatu tempat, dan variabel referensi yang disebut strMain
menunjuk ke sana.
DoSomething(strMain);
Sekarang kami meneruskan referensi itu ke DoSomething
. Kami telah menyampaikannya berdasarkan nilai, jadi itu berarti kami membuat salinannya. Ini adalah tipe referensi, jadi itu berarti kami menyalin referensi, bukan string itu sendiri. Sekarang kita memiliki dua referensi yang masing-masing menunjuk ke nilai yang sama dalam memori.
Inilah metode teratas DoSomething
:
void DoSomething(string strLocal)
Tidak ada ref
kata kunci, jadi strLocal
dan strMain
merupakan dua referensi berbeda yang menunjuk pada nilai yang sama. Jika kita menetapkan kembali strLocal
...
strLocal = "local";
... kami belum mengubah nilai yang disimpan; kami mengambil referensi yang disebut strLocal
dan mengarahkannya ke string baru. Apa yang terjadi strMain
jika kita melakukan itu? Tidak ada. Itu masih menunjuk pada string lama.
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"
Mari kita ubah skenario sebentar. Bayangkan kita tidak bekerja dengan string, tetapi beberapa jenis referensi yang bisa berubah, seperti kelas yang Anda buat.
class MutableThing
{
public int ChangeMe { get; set; }
}
Jika Anda mengikuti referensi objLocal
ke objek yang dituju, Anda dapat mengubah propertinya:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Masih hanya ada satu MutableThing
dalam memori, dan referensi yang disalin dan referensi asli masih mengarah padanya. Properti MutableThing
itu sendiri telah berubah :
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, tapi string tidak bisa diubah! Tidak ada ChangeMe
properti untuk disetel. Anda tidak dapat melakukannya strLocal[3] = 'H'
di C # seperti yang Anda lakukan dengan char
array C-style ; Anda harus membuat string baru sebagai gantinya. Satu-satunya cara untuk mengubahnya strLocal
adalah dengan mengarahkan referensi ke string lain, dan itu berarti tidak ada yang dapat Anda lakukan untuk strLocal
memengaruhi strMain
. Nilainya tidak dapat diubah, dan referensinya adalah salinan.
Untuk membuktikan ada perbedaan, inilah yang terjadi jika Anda memberikan referensi dengan referensi:
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Kali ini, string di Main
benar-benar berubah karena Anda meneruskan referensi tanpa menyalinnya di tumpukan.
Jadi, meskipun string adalah jenis referensi, meneruskannya dengan nilai berarti apa pun yang terjadi di callee tidak akan memengaruhi string di pemanggil. Tetapi karena mereka adalah tipe referensi, Anda tidak perlu menyalin seluruh string dalam memori saat Anda ingin menyebarkannya.
ref
kata kunci. Untuk membuktikan bahwa melewatkan referensi membuat perbedaan, lihat demo ini: rextester.com/WKBG5978
ref
kata kunci memiliki utilitas, saya hanya mencoba menjelaskan mengapa orang mungkin berpikir untuk melewatkan jenis referensi dengan nilai di C # tampak seperti gagasan "tradisional" (yaitu C) melewati referensi (dan meneruskan jenis referensi dengan referensi di C # sepertinya lebih seperti meneruskan referensi ke referensi berdasarkan nilai).
Foo(string bar)
bisa dianggap sebagai Foo(char* bar)
sedangkan Foo(ref string bar)
akan Foo(char** bar)
(atau Foo(char*& bar)
atau Foo(string& bar)
di C ++). Tentu, ini bukan bagaimana Anda harus memikirkannya setiap hari, tetapi itu benar-benar membantu saya akhirnya memahami apa yang terjadi di balik terpal.
String di C # adalah objek referensi yang tidak bisa diubah. Ini berarti bahwa referensi ke sana akan diteruskan (menurut nilai), dan setelah string dibuat, Anda tidak dapat memodifikasinya. Metode yang menghasilkan versi string yang dimodifikasi (substring, versi yang dipotong, dll.) Membuat salinan yang dimodifikasi dari string asli.
String adalah kasus khusus. Setiap contoh tidak dapat diubah. Saat Anda mengubah nilai string, Anda mengalokasikan string baru di memori.
Jadi hanya referensi yang diteruskan ke fungsi Anda, tetapi ketika string diedit, itu menjadi contoh baru dan tidak mengubah contoh lama.
Uri
(kelas) dan Guid
(struct) juga kasus khusus. Saya tidak melihat bagaimana System.String
tindakan seperti "tipe nilai" lebih dari tipe tetap lainnya ... dari asal kelas atau struct.
Uri
& Guid
- Anda dapat menetapkan nilai literal string ke variabel string. String tersebut tampaknya bisa berubah, seperti int
ditugaskan kembali, tetapi itu membuat objek secara implisit - tanpa new
kata kunci.