Как вам кажется, вы знаете, что нижний регистр двух строк и их сравнение - это не то же самое, что сравнение без учета регистра. На то есть масса причин. Например, стандарт Unicode позволяет кодировать текст с диакритическими знаками несколькими способами. Некоторые символы включают в себя как основной символ, так и диакритический знак в одной кодовой точке. Эти символы также могут быть представлены как основной символ, за которым следует комбинированный диакритический знак. Эти два представления одинаковы для всех целей, и сравнение строк с учетом языка и региональных параметров в .NET Framework правильно идентифицирует их как равные либо с CurrentCulture, либо с InvariantCulture (с IgnoreCase или без него). С другой стороны, порядковое сравнение неправильно расценивает их как неравные.
К сожалению, switch
ничего не делает, кроме порядкового сравнения. Порядковое сравнение подходит для определенных типов приложений, например для анализа файла ASCII с жестко заданными кодами, но сравнение порядковых строк неверно для большинства других целей.
То, что я делал в прошлом, чтобы добиться правильного поведения, - это просто смоделировать свой собственный оператор switch. Есть много способов сделать это. Один из способов - создать List<T>
пару строк case и делегатов. В списке можно искать, используя правильное сравнение строк. Когда совпадение найдено, может быть вызван связанный делегат.
Другой вариант - сделать очевидную цепочку if
утверждений. Обычно это оказывается не так плохо, как кажется, поскольку структура очень правильная.
Самое замечательное в этом то, что на самом деле нет никакого снижения производительности при моделировании вашей собственной функциональности переключателя при сравнении со строками. Система не собирается создавать таблицу переходов O (1) так, как она может с целыми числами, поэтому она все равно будет сравнивать каждую строку по одному.
Если есть много случаев для сравнения и производительность является проблемой, то List<T>
описанный выше параметр можно заменить отсортированным словарем или хеш-таблицей. Тогда производительность может потенциально соответствовать или превышать параметр оператора switch.
Вот пример списка делегатов:
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();
}
Конечно, вы, вероятно, захотите добавить некоторые стандартные параметры и, возможно, тип возвращаемого значения в делегат CustomSwitchDestination. И вам захочется придумать лучшие имена!
Если поведение каждого из ваших случаев не позволяет делегировать вызов таким образом, например, если необходимы разные параметры, то вы застряли в цепочке if
статов. Я тоже делал это несколько раз.
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()
илиToLowerInvariant()
? Кроме того, он не сравнивает две неизвестные строки , он сравнивает одну неизвестную строку с одной известной строкой. Таким образом, пока он знает, как жестко закодировать подходящее представление в верхнем или нижнем регистре, блок переключателя должен работать нормально.ToLower()
илиToLowerInvariant()
вернет false.Equals
withStringComparison.InvariantCultureIgnoreCase
вернет истину. Поскольку обе последовательности выглядят одинаково при отображении,ToLower()
версия представляет собой неприятную ошибку, которую нужно отслеживать. Вот почему всегда лучше проводить правильные сравнения строк, даже если вы не турок.Более простой подход - просто уменьшить регистр вашей строки до того, как она войдет в оператор switch, и сделать регистр ниже.
На самом деле, верхняя часть немного лучше с чисто экстремальной наносекундной точки зрения производительности, но менее естественна на вид.
Например:
string s = "house"; switch (s.ToLower()) { case "house": s = "window"; break; }
источник
ToUpper(Invariant)
это не только быстрее, но и надежнее: stackoverflow.com/a/2801521/67824Приносим извинения за это новое сообщение по старому вопросу, но есть новый вариант решения этой проблемы с использованием C # 7 (VS 2017).
C # 7 теперь предлагает «сопоставление с образцом», и его можно использовать для решения этой проблемы следующим образом:
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; }
Это решение также касается проблемы, упомянутой в ответе @Jeffrey L Whitledge, что сравнение строк без учета регистра не то же самое, что сравнение двух строк с нижним регистром.
Кстати, в феврале 2017 года в журнале Visual Studio Magazine была интересная статья, в которой описывалось сопоставление с образцом и его использование в блоках case. Пожалуйста, взгляните: Сопоставление с образцом в блоках регистра C # 7.0
РЕДАКТИРОВАТЬ
В свете ответа @LewisM важно отметить, что у
switch
оператора есть новое интересное поведение. То есть, если вашcase
оператор содержит объявление переменной, то значение, указанное вswitch
части, копируется в переменную, объявленную вcase
. В следующем примере значениеtrue
копируется в локальную переменнуюb
. Кроме того, переменнаяb
не используется и существует только для тогоwhen
, чтобыcase
могло существовать предложение к оператору:switch(true) { case bool b when houseName.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window";): break; }
Как указывает @LewisM, это можно использовать с пользой - это преимущество состоит в том, что сравниваемая вещь фактически находится в
switch
операторе, как и при классическом использованииswitch
оператора. Кроме того, временные значения, объявленные вcase
операторе, могут предотвратить нежелательные или непреднамеренные изменения исходного значения:switch(houseName) { case string hn when hn.Equals("X", StringComparison.InvariantCultureIgnoreCase): windowName = "X-Window"; break; }
источник
switch (houseName)
провести сравнение так же, как и вы, то естьcase var name when name.Equals("MyHouse", ...
switch
значений аргументовcase
временным переменным.case { } when
чтобы не беспокоиться о типе и имени переменной.В некоторых случаях может быть хорошей идеей использовать перечисление. Итак, сначала проанализируйте перечисление (с флагом ignoreCase true), а затем включите перечисление.
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; } }
источник
Расширение ответа @STLDeveloperA. Новый способ оценки операторов без использования нескольких операторов if в C # 7 - это использование оператора Switch сопоставления с шаблоном, аналогично тому, как @STLDeveloper, хотя этот способ включает переключаемую переменную.
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; }
В журнале visual studio есть хорошая статья о блоках регистров с сопоставлением с образцом, на которую стоит обратить внимание.
источник
switch
заявления.case var name when "Bungalow".Equals(name, StringComparison.InvariantCultureIgnoreCase):
написал такой код: так как это может предотвратить исключение нулевой ссылки (где houseName имеет значение null) или, в качестве альтернативы, добавить случай, когда строка сначала будет нулевой.Один из возможных способов - использовать словарь игнорирования регистра с делегатом действия.
string s = null; var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) { {"house", () => s = "window"}, {"house2", () => s = "window2"} }; dic["HouSe"]();
// Обратите внимание, что вызов не возвращает текст, а только заполняет локальную переменную s.
// Если вы хотите вернуть реальный текст, заменить
Action
наFunc<string>
и значение в словаре , чтобы что - то вроде() => "window2"
источник
CurrentCultureIgnoreCase
,OrdinalIgnoreCase
предпочтительнее.Вот решение, которое объединяет решение @Magnus в класс:
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(); } }
Вот пример его использования в простом приложении Windows Form:
var mySwitch = new SwitchCaseIndependent { {"hello", () => MessageBox.Show("hello")}, {"Goodbye", () => MessageBox.Show("Goodbye")}, {"SoLong", () => MessageBox.Show("SoLong")}, }; mySwitch["HELLO"]();
Если вы используете лямбды (как в примере), вы получите замыкания, которые будут захватывать ваши локальные переменные (довольно близко к ощущению, которое вы получаете от оператора switch).
Поскольку он использует словарь под прикрытием, он получает поведение O (1) и не полагается на просмотр списка строк. Конечно, вам нужно составить этот словарь, и это, вероятно, стоит дороже.
Вероятно, имеет смысл добавить простой
bool ContainsCase(string aCase)
метод, который просто вызывает метод словаряContainsKey
.источник
Я надеюсь, что это поможет попытаться преобразовать всю строку в конкретный регистр, в нижний или верхний регистр, и использовать строку в нижнем регистре для сравнения:
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(); } }
источник
Для этого должно быть достаточно:
string s = "houSe"; switch (s.ToLowerInvariant()) { case "house": s = "window"; break; }
Таким образом, сравнение переключателей не зависит от языка и региональных параметров. Насколько я понимаю, это должно дать тот же результат, что и решения C # 7 Pattern-Matching, но более кратко.
источник