Ketika saya meneruskan stringke suatu fungsi, apakah pointer ke konten string dilewatkan, atau seluruh string diteruskan ke fungsi di stack seperti a struct?
Ketika saya meneruskan stringke 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:
strMaintidak diteruskan oleh referensi. Ini adalah tipe referensi, tetapi referensi itu sendiri diteruskan oleh nilai . Setiap kali Anda meneruskan parameter tanpa refkata kunci (tidak menghitung outparameter), 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 refkata 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 Mainmetode ini:
string strMain = "main";
Kami telah membuat dua hal di baris ini: string dengan nilai yang maindisimpan di memori di suatu tempat, dan variabel referensi yang disebut strMainmenunjuk 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 refkata kunci, jadi strLocaldan strMainmerupakan 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 strLocaldan mengarahkannya ke string baru. Apa yang terjadi strMainjika 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 objLocalke objek yang dituju, Anda dapat mengubah propertinya:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Masih hanya ada satu MutableThingdalam memori, dan referensi yang disalin dan referensi asli masih mengarah padanya. Properti MutableThingitu 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 ChangeMeproperti untuk disetel. Anda tidak dapat melakukannya strLocal[3] = 'H'di C # seperti yang Anda lakukan dengan chararray C-style ; Anda harus membuat string baru sebagai gantinya. Satu-satunya cara untuk mengubahnya strLocaladalah dengan mengarahkan referensi ke string lain, dan itu berarti tidak ada yang dapat Anda lakukan untuk strLocalmemengaruhi 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 Mainbenar-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.
refkata kunci. Untuk membuktikan bahwa melewatkan referensi membuat perbedaan, lihat demo ini: rextester.com/WKBG5978
refkata 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.Stringtindakan 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 intditugaskan kembali, tetapi itu membuat objek secara implisit - tanpa newkata kunci.