Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Secara default, C# meneruskan argumen ke fungsi menurut nilai. Pendekatan ini meneruskan salinan variabel ke metode . Untuk jenis nilai (struct), metode mendapatkan salinan nilai. Untuk jenis referensi (class), metode mendapatkan salinan referensi. Anda dapat menggunakan pengubah parameter untuk meneruskan argumen berdasarkan referensi.
Karena struct adalah jenis nilai, meneruskan struct by value ke metode mengirimkan salinan argumen ke metode . Metode ini berfungsi dengan salinan ini. Metode ini tidak dapat mengakses struktur asli dalam metode panggilan dan tidak dapat mengubahnya. Metode hanya dapat mengubah salinan.
Instans kelas adalah jenis referensi, bukan jenis nilai. Saat Anda meneruskan jenis referensi berdasarkan nilai ke metode, metode mendapatkan salinan referensi ke instans. Kedua variabel merujuk ke objek yang sama. Parameter adalah salinan referensi. Metode yang disebut tidak dapat menetapkan ulang instans dalam metode panggilan. Namun, metode yang disebut dapat menggunakan salinan referensi untuk mengakses anggota instans. Jika metode yang disebut mengubah anggota instans, metode panggilan juga melihat perubahan tersebut karena mereferensikan instans yang sama.
Referensi bahasa C# mendokumentasikan versi bahasa C# yang paling baru dirilis. Ini juga berisi dokumentasi awal untuk fitur dalam pratinjau publik untuk rilis bahasa yang akan datang.
Dokumentasi mengidentifikasi fitur apa pun yang pertama kali diperkenalkan dalam tiga versi terakhir bahasa atau dalam pratinjau publik saat ini.
Petunjuk / Saran
Untuk menemukan kapan fitur pertama kali diperkenalkan di C#, lihat artikel tentang riwayat versi bahasa C#.
Meneruskan menurut nilai dan meneruskan berdasarkan referensi
Semua contoh di bagian ini menggunakan dua jenis record berikut untuk mengilustrasikan perbedaan antara jenis class dan jenis struct:
public record struct Point(int X, int Y);
// This doesn't use a primary constructor because the properties implemented for `record` types are
// readonly in record class types. That would prevent the mutations necessary for this example.
public record class Point3D
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
Keluaran dari contoh berikut mengilustrasikan perbedaan antara meneruskan tipe struct dengan nilai dan meneruskan tipe kelas dengan nilai. Kedua Mutate metode mengubah nilai properti argumennya. Saat parameter adalah jenis struct , perubahan tersebut memengaruhi salinan data argumen. Ketika parameter adalah jenis class , perubahan tersebut memengaruhi instans yang dirujuk oleh argumen:
public class PassTypesByValue
{
public static void Mutate(Point pt)
{
Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
pt.X = 19;
pt.Y = 23;
Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
}
public static void Mutate(Point3D pt)
{
Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
pt.X = 19;
pt.Y = 23;
pt.Z = 42;
Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
}
public static void TestPassTypesByValue()
{
Console.WriteLine("===== Value Types =====");
var ptStruct = new Point { X = 1, Y = 2 };
Console.WriteLine($"After initialization:\t\t{ptStruct}");
Mutate(ptStruct);
Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptStruct}");
Console.WriteLine("===== Reference Types =====");
var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };
Console.WriteLine($"After initialization:\t\t{ptClass}");
Mutate(ptClass);
Console.WriteLine($"After called {nameof(Mutate)}:\t\t{ptClass}");
// Output:
// ===== Value Types =====
// After initialization: Point { X = 1, Y = 2 }
// Enter Mutate: Point { X = 1, Y = 2 }
// Exit Mutate: Point { X = 19, Y = 23 }
// After called Mutate: Point { X = 1, Y = 2 }
// ===== Reference Types =====
// After initialization: Point3D { X = 1, Y = 2, Z = 3 }
// Enter Mutate: Point3D { X = 1, Y = 2, Z = 3 }
// Exit Mutate: Point3D { X = 19, Y = 23, Z = 42 }
// After called Mutate: Point3D { X = 19, Y = 23, Z = 42 }
}
}
Pengubah ref adalah salah satu cara untuk meneruskan argumen dengan mereferensikan ke metode. Kode berikut mereplikasi contoh sebelumnya, tetapi meneruskan parameter berdasarkan referensi. Modifikasi yang dilakukan pada jenis struct terlihat dalam metode panggilan ketika struktur diteruskan dengan referensi. Tidak ada perubahan semantik saat jenis referensi dikirimkan sebagai referensi.
public class PassTypesByReference
{
public static void Mutate(ref Point pt)
{
Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
pt.X = 19;
pt.Y = 23;
Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
}
public static void Mutate(ref Point3D pt)
{
Console.WriteLine($"\tEnter {nameof(Mutate)}:\t\t{pt}");
pt.X = 19;
pt.Y = 23;
pt.Z = 42;
Console.WriteLine($"\tExit {nameof(Mutate)}:\t\t{pt}");
}
public static void TestPassTypesByReference()
{
Console.WriteLine("===== Value Types =====");
var pStruct = new Point { X = 1, Y = 2 };
Console.WriteLine($"After initialization:\t\t{pStruct}");
Mutate(ref pStruct);
Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pStruct}");
Console.WriteLine("===== Reference Types =====");
var pClass = new Point3D { X = 1, Y = 2, Z = 3 };
Console.WriteLine($"After initialization:\t\t{pClass}");
Mutate(ref pClass);
Console.WriteLine($"After called {nameof(Mutate)}:\t\t{pClass}");
// Output:
// ===== Value Types =====
// After initialization: Point { X = 1, Y = 2 }
// Enter Mutate: Point { X = 1, Y = 2 }
// Exit Mutate: Point { X = 19, Y = 23 }
// After called Mutate: Point { X = 19, Y = 23 }
// ===== Reference Types =====
// After initialization: Point3D { X = 1, Y = 2, Z = 3 }
// Enter Mutate: Point3D { X = 1, Y = 2, Z = 3 }
// Exit Mutate: Point3D { X = 19, Y = 23, Z = 42 }
// After called Mutate: Point3D { X = 19, Y = 23, Z = 42 }
}
}
Contoh sebelumnya memodifikasi properti parameter. Metode juga dapat menetapkan ulang parameter ke nilai baru. Penetapan ulang berperilaku berbeda untuk tipe struct dan class saat dilewatkan sebagai nilai atau referensi. Contoh berikut menunjukkan bagaimana tipe struct dan tipe kelas berperilaku ketika parameter yang dikirimkan sebagai nilai ditetapkan ulang.
public class PassByValueReassignment
{
public static void Reassign(Point pt)
{
Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
pt = new Point { X = 13, Y = 29 };
Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
}
public static void Reassign(Point3D pt)
{
Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
pt = new Point3D { X = 13, Y = 29, Z = -42 };
Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
}
public static void TestPassByValueReassignment()
{
Console.WriteLine("===== Value Types =====");
var ptStruct = new Point { X = 1, Y = 2 };
Console.WriteLine($"After initialization:\t\t{ptStruct}");
Reassign(ptStruct);
Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");
Console.WriteLine("===== Reference Types =====");
var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };
Console.WriteLine($"After initialization:\t\t{ptClass}");
Reassign(ptClass);
Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");
// Output:
// ===== Value Types =====
// After initialization: Point { X = 1, Y = 2 }
// Enter Reassign: Point { X = 1, Y = 2 }
// Exit Reassign: Point { X = 13, Y = 29 }
// After called Reassign: Point { X = 1, Y = 2 }
// ===== Reference Types =====
// After initialization: Point3D { X = 1, Y = 2, Z = 3 }
// Enter Reassign: Point3D { X = 1, Y = 2, Z = 3 }
// Exit Reassign: Point3D { X = 13, Y = 29, Z = -42 }
// After called Reassign: Point3D { X = 1, Y = 2, Z = 3 }
}
}
Sampel sebelumnya menunjukkan bahwa saat Anda menetapkan ulang parameter ke nilai baru, perubahan tersebut tidak terlihat dari metode panggilan, terlepas dari apakah jenisnya adalah jenis nilai atau jenis referensi. Contoh berikut menunjukkan perilaku saat Anda menetapkan ulang parameter yang diterima metode dengan referensi:
public class PassByReferenceReassignment
{
public static void Reassign(ref Point pt)
{
Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
pt = new Point { X = 13, Y = 29 };
Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
}
public static void Reassign(ref Point3D pt)
{
Console.WriteLine($"\tEnter {nameof(Reassign)}:\t\t{pt}");
pt = new Point3D { X = 13, Y = 29, Z = -42 };
Console.WriteLine($"\tExit {nameof(Reassign)}:\t\t{pt}");
}
public static void TestPassByReferenceReassignment()
{
Console.WriteLine("===== Value Types =====");
var ptStruct = new Point { X = 1, Y = 2 };
Console.WriteLine($"After initialization:\t\t{ptStruct}");
Reassign(ref ptStruct);
Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptStruct}");
Console.WriteLine("===== Reference Types =====");
var ptClass = new Point3D { X = 1, Y = 2, Z = 3 };
Console.WriteLine($"After initialization:\t\t{ptClass}");
Reassign(ref ptClass);
Console.WriteLine($"After called {nameof(Reassign)}:\t\t{ptClass}");
// Output:
// ===== Value Types =====
// After initialization: Point { X = 1, Y = 2 }
// Enter Reassign: Point { X = 1, Y = 2 }
// Exit Reassign: Point { X = 13, Y = 29 }
// After called Reassign: Point { X = 13, Y = 29 }
// ===== Reference Types =====
// After initialization: Point3D { X = 1, Y = 2, Z = 3 }
// Enter Reassign: Point3D { X = 1, Y = 2, Z = 3 }
// Exit Reassign: Point3D { X = 13, Y = 29, Z = -42 }
// After called Reassign: Point3D { X = 13, Y = 29, Z = -42 }
}
}
Contoh sebelumnya menunjukkan bagaimana mengubah kembali nilai parameter yang dikirim melalui referensi terlihat dalam konteks panggilan.
Konteks referensi dan nilai yang aman
Metode dapat menyimpan nilai parameter dalam bidang. Saat Anda meneruskan parameter menurut nilai, biasanya aman. Metode menyalin nilai, dan jenis referensi dapat dijangkau ketika metode menyimpannya di bidang. Meneruskan parameter dengan referensi dengan aman mengharuskan pengkompilasi untuk menentukan kapan aman untuk menetapkan referensi ke variabel baru. Untuk setiap ekspresi, pengkompilasi menentukan konteks aman yang mengikat akses ke ekspresi atau variabel. Pengkompilasi menggunakan dua cakupan: konteks aman dan konteks ref-safe.
- Konteks aman menentukan cakupan di mana ekspresi apa pun dapat diakses dengan aman.
- Konteks ref-safe mendefinisikan cakupan di mana referensi ke ekspresi apa pun dapat diakses atau dimodifikasi dengan aman.
Secara informal, Anda dapat menganggap cakupan ini sebagai mekanisme untuk memastikan kode Anda tidak pernah mengakses atau memodifikasi referensi yang tidak lagi valid. Referensi valid selama mengacu pada objek atau struktur yang valid. Konteks aman menentukan kapan variabel dapat ditetapkan atau ditetapkan ulang. Konteks ref-safe menentukan kapan variabel dapat ditetapkan ref atau ref ditetapkan kembali. Penugasan menetapkan variabel ke nilai baru; penetapan ref menetapkan variabel untuk merujuk ke lokasi penyimpanan yang berbeda.
Parameter referensi
Untuk meneruskan argumen berdasarkan referensi alih-alih berdasarkan nilai, gunakan salah satu pengubah berikut dalam deklarasi parameter:
-
ref: Inisialisasi argumen sebelum memanggil metode . Metode ini dapat menetapkan nilai baru ke parameter, tetapi tidak diperlukan. -
out: Metode panggilan tidak perlu menginisialisasi argumen sebelum memanggil metode . Metode harus menetapkan nilai ke parameter . -
ref readonly: Inisialisasi argumen sebelum memanggil metode . Metode ini tidak dapat menetapkan nilai baru ke parameter . -
in: Inisialisasi argumen sebelum memanggil metode . Metode ini tidak dapat menetapkan nilai baru ke parameter . Pengkompilasi mungkin membuat variabel sementara untuk menyimpan salinan argumen keinparameter.
Parameter yang diteruskan oleh referensi adalah variabel referensi. Ini tidak memiliki nilainya sendiri. Sebaliknya, ini mengacu pada variabel yang berbeda yang disebut referensinya. Anda dapat menetapkan ulang variabel referensi, yang mengubah referensinya.
Anggota kelas tidak dapat memiliki tanda tangan yang hanya berbeda dengan ref, , ref readonly, inatau out. Kesalahan kompilator terjadi jika satu-satunya perbedaan antara dua anggota jenis adalah bahwa satu anggota memiliki ref parameter dan anggota lain memiliki outparameter , , ref readonlyatau in . Namun, Anda dapat membebani metode berlebihan ketika satu metode memiliki refparameter , , ref readonlyin, atau out dan metode lainnya memiliki parameter yang diteruskan oleh nilai, seperti yang ditunjukkan dalam contoh berikut. Dalam situasi lain yang memerlukan pencocokan tanda tangan, seperti menyembunyikan atau menimpa, in, , refref readonly, dan out merupakan bagian dari tanda tangan dan tidak cocok satu sama lain.
Ketika parameter memiliki salah satu pengubah sebelumnya, argumen yang sesuai dapat memiliki pengubah yang kompatibel:
- Argumen untuk
refparameter harus menyertakan pengubahref. - Argumen untuk
outparameter harus menyertakan pengubahout. - Argumen untuk
inparameter dapat secara opsional menyertakan pengubahin. Jika pengubahrefdigunakan pada argumen sebagai gantinya, pengkompilasi mengeluarkan peringatan. - Argumen untuk
ref readonlyparameter harus menyertakan pengubahinatauref, tetapi tidak keduanya. Jika tidak ada pengubah yang disertakan, pengkompilasi mengeluarkan peringatan.
Saat Anda menggunakan pengubah ini, mereka menjelaskan bagaimana argumen digunakan:
-
refberarti metode dapat membaca atau menulis nilai argumen. -
outberarti metode menetapkan nilai argumen. -
ref readonlyberarti metode membaca, tetapi tidak dapat menulis nilai argumen. Argumen harus diteruskan oleh referensi. -
inberarti metode membaca, tetapi tidak dapat menulis nilai argumen. Argumen diteruskan oleh referensi atau melalui variabel sementara.
Anda tidak dapat menggunakan pengubah parameter sebelumnya dalam jenis metode berikut:
- Metode asinkron, yang Anda tentukan menggunakan pengubah async.
- Metode iterator, yang mencakup yield return atau pernyataan
yield break.
Anggota ekstensi juga memiliki batasan penggunaan kata kunci argumen ini:
- Kata
outkunci tidak dapat digunakan pada argumen pertama dari metode ekstensi. - Kata
refkunci tidak dapat digunakan pada argumen pertama metode ekstensi ketika argumen bukanstruct, atau jenis generik yang tidak dibatasi untuk menjadi struct. - Kata
ref readonlykunci danintidak dapat digunakan kecuali argumen pertama adalahstruct. - Kata
ref readonlykunci danintidak dapat digunakan pada jenis generik apa pun, bahkan ketika dibatasi menjadi struct.
Properti bukan variabel. Ini adalah metode. Anda tidak dapat menggunakan properti sebagai argumen untuk ref parameter.
ref pengubah parameter
Untuk menggunakan parameter ref, definisi metode dan metode panggilan harus menggunakan kata kunci ref secara eksplisit. (Kecuali bahwa metode panggilan bisa menghilangkan ref saat melakukan panggilan COM.)
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
Anda harus menginisialisasi argumen sebelum meneruskannya ke ref parameter.
out pengubah parameter
Untuk menggunakan parameter out, definisi metode dan metode panggilan harus secara eksplisit menggunakan kata kunci out. Contohnya:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
Anda tidak perlu menginisialisasi variabel yang diteruskan sebagai out argumen sebelum panggilan metode. Namun, metode yang disebut harus menetapkan nilai sebelum dikembalikan.
Metode dekonstruksi mendeklarasikan parameternya dengan pengubah out untuk mengembalikan beberapa nilai. Metode lain dapat mengembalikan tuple nilai untuk beberapa nilai pengembalian.
Anda dapat mendeklarasikan variabel dalam pernyataan terpisah sebelum meneruskannya sebagai out argumen. Anda juga dapat mendeklarasikan out variabel dalam daftar argumen panggilan metode, bukan dalam deklarasi variabel terpisah.
out deklarasi variabel menghasilkan kode yang lebih ringkas, dapat dibaca, dan juga mencegah Anda menetapkan nilai secara tidak sengaja ke variabel sebelum panggilan metode. Contoh berikut menentukan number variabel dalam panggilan ke metode Int32.TryParse .
string numberAsString = "1640";
if (Int32.TryParse(numberAsString, out int number))
Console.WriteLine($"Converted '{numberAsString}' to {number}");
else
Console.WriteLine($"Unable to convert '{numberAsString}'");
// The example displays the following output:
// Converted '1640' to 1640
Anda juga dapat mendeklarasikan variabel lokal yang diketik secara implisit.
ref readonly Pengubah
Deklarasi metode memerlukan pengubah ref readonly . Pengubah di situs panggilan bersifat opsional. Anda dapat menggunakan pengubah in atau ref . Pengubah ref readonly tidak valid di situs panggilan. Pengubah yang Anda gunakan di situs panggilan dapat membantu menjelaskan karakteristik argumen. Anda hanya dapat menggunakan ref jika argumen adalah variabel dan dapat ditulis. Anda hanya dapat menggunakan in saat argumen adalah variabel. Variabel mungkin dapat ditulis atau dibaca secara baca-saja. Anda tidak dapat menambahkan salah satu pengubah jika argumen bukan variabel tetapi merupakan ekspresi. Contoh berikut menunjukkan kondisi ini. Metode berikut menggunakan pengubah ref readonly untuk menunjukkan bahwa struktur besar harus diteruskan oleh referensi karena alasan performa:
public static void ForceByRef(ref readonly OptionStruct thing)
{
// elided
}
Anda dapat memanggil metode dengan menggunakan pengubah ref atau in . Jika Anda menghilangkan pengubah, pengkompilasi mengeluarkan peringatan. Saat argumen adalah ekspresi, bukan variabel, Anda tidak dapat menambahkan in pengubah atau ref , jadi Anda harus menekan peringatan:
ForceByRef(in options);
ForceByRef(ref options);
ForceByRef(options); // Warning! variable should be passed with `ref` or `in`
ForceByRef(new OptionStruct()); // Warning, but an expression, so no variable to reference
Jika variabel adalah readonly variabel, Anda harus menggunakan pengubah in . Pengkompilasi mengeluarkan kesalahan jika Anda menggunakan pengubah sebagai gantinya ref .
Pengubah ref readonly menunjukkan bahwa metode mengharapkan argumen menjadi variabel daripada ekspresi yang bukan variabel. Contoh ekspresi yang bukan variabel adalah konstanta, nilai pengembalian metode, dan properti. Jika argumen bukan variabel, pengkompilasi mengeluarkan peringatan.
in pengubah parameter
Pengubah in diperlukan dalam deklarasi metode tetapi tidak perlu di situs panggilan.
var largeStruct = new LargeStruct { Value1 = 42, Value2 = 3.14, Value3 = "Hello" };
// Using 'in' avoids copying the large struct and prevents modification
ProcessLargeStruct(in largeStruct);
Console.WriteLine($"Original value unchanged: {largeStruct.Value1}");
// Without 'in', the struct would be copied (less efficient for large structs)
ProcessLargeStructByValue(largeStruct);
Console.WriteLine($"Original value still unchanged: {largeStruct.Value1}");
void ProcessLargeStruct(in LargeStruct data)
{
// Can read the values
Console.WriteLine($"Processing: {data.Value1}, {data.Value2}, {data.Value3}");
// Uncomment the following line to see error CS8331
// data.Value1 = 99; // Compilation error: cannot assign to 'in' parameter
}
void ProcessLargeStructByValue(LargeStruct data)
{
// This method receives a copy of the struct
Console.WriteLine($"Processing copy: {data.Value1}, {data.Value2}, {data.Value3}");
// Modifying the copy doesn't affect the original
data.Value1 = 99;
}
Pengubah in memungkinkan pengompilasi untuk membuat variabel sementara untuk argumen dan meneruskan referensi baca-saja ke argumen tersebut. Pengkompilasi selalu membuat variabel sementara ketika argumen harus dikonversi, ketika ada konversi implisit dari jenis argumen, atau ketika argumen adalah nilai yang bukan variabel. Misalnya, ketika argumen adalah nilai harfiah, atau nilai yang dikembalikan dari pengakses properti. Saat API Anda mengharuskan argumen diteruskan oleh referensi, pilih ref readonly pengubah alih-alih pengubah in .
Anda dapat memperoleh pengoptimalan performa dengan menentukan metode dengan in parameter. Beberapa struct argumen jenis mungkin berukuran besar, dan ketika Anda memanggil metode dalam perulangan yang ketat atau jalur kode penting, biaya penyalinan struktur tersebut sangat besar. Deklarasikan in parameter untuk menentukan bahwa Anda dapat meneruskan argumen dengan aman berdasarkan referensi karena metode yang disebut tidak mengubah status argumen tersebut. Meneruskan argumen tersebut dengan referensi menghindari salinan mahal (potensial). Anda secara eksplisit menambahkan pengubah in di situs panggilan untuk memastikan argumen diteruskan berdasarkan referensi, bukan berdasarkan nilai. Secara eksplisit menggunakan in memiliki dua efek berikut:
- Menentukan
indi situs panggilan memaksa pengkompilasi untuk memilih metode yang ditentukan dengan parameter yangincocok. Jika tidak, ketika dua metode berbeda di hadapaninsaja, kelebihan beban berdasarkan nilai adalah kecocokan yang lebih baik. - Dengan menentukan
in, Anda menyatakan niat Anda untuk meneruskan argumen berdasarkan referensi. Argumen yang digunakan denganinharus mewakili lokasi yang dapat langsung dirujuk. Aturan umum yang sama untukoutargumen danrefberlaku: Anda tidak dapat menggunakan konstanta, properti biasa, atau ekspresi lain yang menghasilkan nilai. Jika tidak, menghilangkan di situs panggilan menginformasikan pengkompilasi bahwa tidak masalah untuk membuat variabel sementara untuk diteruskanindengan referensi baca-saja ke metode . Pengkompilasi membuat variabel sementara untuk mengatasi beberapa batasan denganinargumen:- Variabel sementara memungkinkan konstanta waktu kompilasi sebagai parameter
in. - Variabel sementara mengizinkan properti, atau ekspresi lain untuk parameter
in. - Variabel sementara memungkinkan argumen di mana ada konversi implisit dari jenis argumen ke jenis parameter.
- Variabel sementara memungkinkan konstanta waktu kompilasi sebagai parameter
Dalam semua instans sebelumnya, kompilator membuat variabel sementara yang menyimpan nilai konstanta, properti, atau ekspresi lainnya.
Kode berikut menunjukkan berbagai aturan ini:
static void Method(in int argument)
{
// implementation removed
}
Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`
Sekarang, misalkan metode lain yang menggunakan argumen berdasarkan nilai tersedia. Hasilnya berubah seperti yang ditunjukkan dalam kode berikut:
static void Method(int argument)
{
// implementation removed
}
static void Method(in int argument)
{
// implementation removed
}
Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`
Satu-satunya panggilan metode saat argumen diteruskan berdasarkan referensi adalah yang terakhir.
Catatan
Kode sebelumnya menggunakan int sebagai jenis argumen untuk kesederhanaan. Karena int tidak lebih besar dari referensi di sebagian besar komputer modern, tidak ada manfaat untuk meneruskan satu int sebagai referensi baca-saja.
params Pengubah
Parameter dengan params kata kunci harus menjadi parameter terakhir dalam deklarasi metode. Anda hanya dapat menggunakan satu params kata kunci dalam deklarasi metode.
Anda harus mendeklarasikan params parameter sebagai jenis koleksi. Jenis koleksi yang dikenali meliputi:
-
Jenis
T[]array dimensi tunggal, di mana jenis elemen adalahT. - Jenis rentang:
System.Span<T>System.ReadOnlySpan<T>
Dalam jenis ini, jenis elemennya adalahT.
- Jenis dengan metode buat yang dapat diakses yang memiliki jenis elemen yang sesuai. Metode buat menggunakan atribut yang sama dengan ekspresi koleksi.
- Jenis struct atau class yang mengimplementasikan System.Collections.Generic.IEnumerable<T> di mana:
- Jenis memiliki konstruktor yang dapat Anda panggil tanpa argumen, dan konstruktor setidaknya dapat diakses sebagai anggota yang mendeklarasikan.
- Jenis memiliki metode
Addinstans (bukan ekstensi) di mana:- Metode dapat dipanggil dengan argumen nilai tunggal.
- Jika metode umum, argumen jenis dapat disimpulkan dari argumen .
- Metode ini setidaknya dapat diakses sebagai anggota yang mendeklarasikan. Dalam hal ini, jenis elemen adalah jenis iterasi dari jenis .
- Jenis antarmuka:
Sebelum C# 13, Anda harus menggunakan array dimensi tunggal untuk parameter .
Saat Anda memanggil metode dengan parameter params, Anda dapat meneruskan:
- Daftar argumen yang dipisahkan koma dari jenis elemen array.
- Kumpulan argumen dari jenis yang ditentukan.
- Tidak ada argumen. Jika Anda tidak mengirim argumen, panjang daftar
paramsadalah nol.
Contoh berikut menunjukkan berbagai cara untuk mengirim argumen ke params parameter.
public static void ParamsModifierExample(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
System.Console.Write(list[i] + " ");
}
System.Console.WriteLine();
}
public static void ParamsModifierObjectExample(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
System.Console.Write(list[i] + " ");
}
System.Console.WriteLine();
}
public static void TryParamsCalls()
{
// You can send a comma-separated list of arguments of the
// specified type.
ParamsModifierExample(1, 2, 3, 4);
ParamsModifierObjectExample(1, 'a', "test");
// A params parameter accepts zero or more arguments.
// The following calling statement displays only a blank line.
ParamsModifierObjectExample();
// An array argument can be passed, as long as the array
// type matches the parameter type of the method being called.
int[] myIntArray = { 5, 6, 7, 8, 9 };
ParamsModifierExample(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
ParamsModifierObjectExample(myObjArray);
// The following call causes a compiler error because the object
// array cannot be converted into an integer array.
//ParamsModifierExample(myObjArray);
// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
ParamsModifierObjectExample(myIntArray);
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
Resolusi kelebihan beban dapat menyebabkan ambiguitas ketika argumen untuk params parameter adalah jenis koleksi. Jenis koleksi argumen harus dapat dikonversi ke jenis koleksi parameter. Ketika kelebihan beban yang berbeda memberikan konversi yang lebih baik untuk parameter tersebut, metode tersebut mungkin lebih baik. Namun, jika argumen ke parameter adalah elemen diskrit atau hilang, semua kelebihan beban dengan jenis parameter yang berbeda params sama untuk parameter tersebutparams.
Untuk informasi selengkapnya, lihat bagian tentang daftar Argumen