Seperti yang Anda ketahui, menurunkan dua string dan membandingkannya tidak sama dengan melakukan perbandingan kasus pengabaian. Ada banyak alasan untuk ini. Misalnya, standar Unicode memungkinkan teks dengan diakritik dikodekan dengan berbagai cara. Beberapa karakter menyertakan karakter dasar dan diakritik dalam satu titik kode. Karakter ini juga dapat direpresentasikan sebagai karakter dasar yang diikuti oleh karakter diakritik gabungan. Kedua representasi ini sama untuk semua tujuan, dan perbandingan string yang sadar budaya dalam .NET Framework akan mengidentifikasi mereka dengan benar sebagai sama, baik dengan CurrentCulture atau InvariantCulture (dengan atau tanpa IgnoreCase). Sebaliknya, perbandingan ordinal akan secara keliru menganggapnya tidak setara.
Sayangnya, switch
tidak melakukan apa pun kecuali perbandingan ordinal. Perbandingan ordinal baik-baik saja untuk jenis aplikasi tertentu, seperti mem-parsing file ASCII dengan kode yang ditentukan secara kaku, tetapi perbandingan string ordinal salah untuk sebagian besar kegunaan lain.
Apa yang telah saya lakukan di masa lalu untuk mendapatkan perilaku yang benar hanyalah tiruan pernyataan sakelar saya sendiri. Ada banyak cara untuk melakukannya. Salah satu caranya adalah dengan membuat List<T>
sepasang string kasus dan delegasi. Daftar tersebut dapat dicari dengan menggunakan perbandingan string yang tepat. Ketika kecocokan ditemukan maka delegasi terkait dapat dipanggil.
Pilihan lainnya adalah melakukan rangkaian if
pernyataan yang jelas . Ini biasanya ternyata tidak seburuk kedengarannya, karena strukturnya sangat teratur.
Hal yang hebat tentang ini adalah bahwa sebenarnya tidak ada penalti kinerja dalam meniru fungsi sakelar Anda sendiri saat membandingkan dengan string. Sistem tidak akan membuat tabel lompat O (1) seperti yang dapat dilakukan dengan bilangan bulat, jadi sistem akan membandingkan setiap string satu per satu.
Jika ada banyak kasus untuk dibandingkan, dan kinerja menjadi masalah, maka List<T>
opsi yang dijelaskan di atas dapat diganti dengan kamus yang diurutkan atau tabel hash. Kemudian kinerja berpotensi cocok atau melebihi opsi pernyataan sakelar.
Berikut adalah contoh daftar delegasi:
delegate void CustomSwitchDestination();
List<KeyValuePair<string, CustomSwitchDestination>> customSwitchList;
CustomSwitchDestination defaultSwitchDestination = new CustomSwitchDestination(NoMatchFound);
void CustomSwitch(string value)
{
foreach (var switchOption in customSwitchList)
if (switchOption.Key.Equals(value, StringComparison.InvariantCultureIgnoreCase))
{
switchOption.Value.Invoke();
return;
}
defaultSwitchDestination.Invoke();
}
Tentu saja, Anda mungkin ingin menambahkan beberapa parameter standar dan mungkin tipe pengembalian ke delegasi CustomSwitchDestination. Dan Anda pasti ingin membuat nama yang lebih baik!
Jika perilaku setiap kasus Anda tidak setuju untuk mendelegasikan pemanggilan dengan cara ini, seperti jika parameter differnt diperlukan, maka Anda terjebak dengan if
pernyataan yang dirantai . Saya juga melakukan ini beberapa kali.
if (s.Equals("house", StringComparison.InvariantCultureIgnoreCase))
{
s = "window";
}
else if (s.Equals("business", StringComparison.InvariantCultureIgnoreCase))
{
s = "really big window";
}
else if (s.Equals("school", StringComparison.InvariantCultureIgnoreCase))
{
s = "broken window";
}
ToUpperInvariant()
atauToLowerInvariant()
? Juga, dia tidak membandingkan dua string yang tidak diketahui , dia membandingkan satu string yang tidak diketahui dengan satu string yang dikenal. Jadi, selama dia tahu cara membuat kode keras untuk representasi huruf besar atau kecil yang sesuai maka blok sakelar harus berfungsi dengan baik.ToLower()
atauToLowerInvariant()
akan mengembalikan salah.Equals
denganStringComparison.InvariantCultureIgnoreCase
akan kembali benar. Karena kedua urutan terlihat identik saat ditampilkan,ToLower()
versinya adalah bug yang buruk untuk dilacak. Inilah mengapa selalu yang terbaik adalah melakukan perbandingan senar yang tepat, meskipun Anda bukan orang Turki.Pendekatan yang lebih sederhana hanya menurunkan string Anda sebelum masuk ke pernyataan switch, dan memiliki case yang lebih rendah.
Sebenarnya, bagian atas sedikit lebih baik dari sudut pandang kinerja nanodetik ekstrim murni, tetapi kurang alami untuk dilihat.
Misalnya:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
sumber
ToUpper(Invariant)
tidak hanya lebih cepat, tetapi lebih dapat diandalkan: stackoverflow.com/a/2801521/67824Maaf untuk posting baru ini untuk pertanyaan lama, tetapi ada opsi baru untuk menyelesaikan masalah ini menggunakan C # 7 (VS 2017).
C # 7 sekarang menawarkan "pencocokan pola", dan ini dapat digunakan untuk mengatasi masalah ini sebagai berikut:
string houseName = "house"; // value to be tested, ignoring case string windowName; // switch block will set value here switch (true) { case bool b when houseName.Equals("MyHouse", StringComparison.InvariantCultureIgnoreCase): windowName = "MyWindow"; break; case bool b when houseName.Equals("YourHouse", StringComparison.InvariantCultureIgnoreCase): windowName = "YourWindow"; break; case bool b when houseName.Equals("House", StringComparison.InvariantCultureIgnoreCase): windowName = "Window"; break; default: windowName = null; break; }
Solusi ini juga menangani masalah yang disebutkan dalam jawaban oleh @Jeffrey L Whitledge bahwa perbandingan string yang tidak peka huruf besar / kecil tidak sama dengan membandingkan dua string dengan huruf kecil.
Ngomong-ngomong, ada artikel menarik di bulan Februari 2017 di Majalah Visual Studio yang menjelaskan tentang pencocokan pola dan bagaimana cara menggunakannya dalam kasus blok Mohon dilihat: Pencocokan Pola di Blok Kasus C # 7.0
EDIT
Sehubungan dengan jawaban @ LewisM, penting untuk menunjukkan bahwa
switch
pernyataan tersebut memiliki beberapa perilaku baru yang menarik. Artinya jikacase
pernyataan Anda berisi deklarasi variabel, maka nilai yang ditentukan diswitch
bagian tersebut disalin ke variabel yang dideklarasikan dicase
. Dalam contoh berikut, nilaitrue
disalin ke variabel lokalb
. Selanjutnya, variabelb
tidak digunakan, dan hanya ada sehinggawhen
klausacase
pernyataan bisa ada:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Seperti yang ditunjukkan oleh @LewisM, ini dapat digunakan untuk mendapatkan keuntungan - manfaatnya adalah bahwa hal yang dibandingkan sebenarnya ada dalam
switch
pernyataan, seperti halnya dengan penggunaan klasik dariswitch
pernyataan tersebut. Selain itu, nilai sementara yang dideklarasikan dalamcase
pernyataan dapat mencegah perubahan yang tidak diinginkan atau tidak disengaja ke nilai aslinya:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
sumber
switch (houseName)
melakukan perbandingan yang serupa dengan cara Anda melakukannya, yaitucase var name when name.Equals("MyHouse", ...
switch
nilai argumen kecase
variabel sementara.case { } when
sehingga Anda tidak perlu khawatir tentang jenis dan nama variabel.Dalam beberapa kasus, mungkin ada baiknya menggunakan enum. Jadi pertama-tama parsing enum (dengan flag ignoreCase true) dan kemudian mengaktifkan enum.
SampleEnum Result; bool Success = SampleEnum.TryParse(inputText, true, out Result); if(!Success){ //value was not in the enum values }else{ switch (Result) { case SampleEnum.Value1: break; case SampleEnum.Value2: break; default: //do default behaviour break; } }
sumber
Perpanjangan jawaban oleh @STLDeveloperA. Cara baru untuk melakukan evaluasi pernyataan tanpa beberapa pernyataan if pada c # 7 menggunakan pernyataan Switch pencocokan pola, mirip dengan cara @STLDeveloper meskipun cara ini mengaktifkan variabel yang diaktifkan
string houseName = "house"; // value to be tested string s; switch (houseName) { case var name when string.Equals(name, "Bungalow", StringComparison.InvariantCultureIgnoreCase): s = "Single glazed"; break; case var name when string.Equals(name, "Church", StringComparison.InvariantCultureIgnoreCase): s = "Stained glass"; break; ... default: s = "No windows (cold or dark)"; break; }
Majalah studio visual memiliki artikel bagus tentang blok kasus pencocokan pola yang mungkin menarik untuk dilihat.
sumber
switch
pernyataan baru .case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
karena ini dapat mencegah pengecualian referensi null (di mana houseName adalah null), atau sebagai alternatif menambahkan kasus untuk string menjadi null terlebih dahulu.Salah satu cara yang mungkin adalah menggunakan kamus kasus abaikan dengan delegasi tindakan.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Perhatikan bahwa panggilan tidak mengembalikan teks, tetapi hanya mengisi variabel lokal s.
// Jika Anda ingin mengembalikan teks yang sebenarnya, ganti
Action
keFunc<string>
dan nilai dalam kamus menjadi sesuatu seperti() => "window2"
sumber
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
lebih disukai.Berikut adalah solusi yang membungkus solusi @Magnus dalam sebuah kelas:
public class SwitchCaseIndependent : IEnumerable<KeyValuePair<string, Action>> { private readonly Dictionary<string, Action> _cases = new Dictionary<string, Action>(StringComparer.OrdinalIgnoreCase); public void Add(string theCase, Action theResult) { _cases.Add(theCase, theResult); } public Action this[string whichCase] { get { if (!_cases.ContainsKey(whichCase)) { throw new ArgumentException($"Error in SwitchCaseIndependent, \"{whichCase}\" is not a valid option"); } //otherwise return _cases[whichCase]; } } public IEnumerator<KeyValuePair<string, Action>> GetEnumerator() { return _cases.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _cases.GetEnumerator(); } }
Berikut adalah contoh penggunaannya di aplikasi Formulir Windows sederhana:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Jika Anda menggunakan lambda (seperti contoh), Anda mendapatkan penutupan yang akan menangkap variabel lokal Anda (cukup mirip dengan perasaan yang Anda dapatkan dari pernyataan switch).
Karena menggunakan Dictionary di bawah sampulnya, ia mendapat perilaku O (1) dan tidak bergantung pada berjalan melalui daftar string. Tentu saja, Anda perlu membuat kamus itu, dan itu mungkin lebih mahal.
Mungkin masuk akal untuk menambahkan
bool ContainsCase(string aCase)
metode sederhana yang hanya memanggil metode kamusContainsKey
.sumber
Saya harap ini membantu mencoba mengubah seluruh string menjadi huruf kecil atau huruf besar dan menggunakan string Huruf kecil untuk perbandingan:
public string ConvertMeasurements(string unitType, string value) { switch (unitType.ToLower()) { case "mmol/l": return (Double.Parse(value) * 0.0555).ToString(); case "mg/dl": return (double.Parse(value) * 18.0182).ToString(); } }
sumber
Ini harus cukup untuk melakukan ini:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
Perbandingan sakelar dengan demikian budaya tidak berubah. Sejauh yang saya bisa lihat, ini akan mencapai hasil yang sama seperti solusi Pencocokan Pola C # 7, tetapi lebih ringkas.
sumber
10 tahun kemudian, dengan pencocokan pola C #, Anda dapat melakukan sesuatu seperti:
private string NormalisePropertyType(string propertyType) => true switch { true when string.IsNullOrWhiteSpace(propertyType) => propertyType, true when "house".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "house", true when "window".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "window", true when "door".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "door", true when "roof".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "roof", true when "chair".Equals(propertyType, StringComparison.OrdinalIgnoreCase) => "chair", _ => propertyType };
sumber