2010-02-25 2 views
54

Если у меня есть оператор switch-case, где объект в коммутаторе является строкой, можно ли в любом случае игнорировать сравнение Case?Как сделать заявление оператора C# использовать IgnoreCase

У меня есть, например:

string s = "house"; 
switch (s) 
{ 
    case "houSe": s = "window"; 
} 

Уилла сек значение приобретает "окно". Как переопределить оператор switch-case, чтобы он сравнивал строки с помощью ignoreCase?

ответ

49

Как вы, кажется, чтобы быть в курсе, lowercasing две строки и сравнивая их не то же самое, как делают в сравнение игнорирований случай. Для этого есть много причин. Например, стандарт Unicode позволяет кодировать текст с диакритикой несколькими способами. Некоторые символы включают в себя как базовый, так и диакритический символы в одной кодовой точке. Эти символы также могут быть представлены в качестве базового символа, сопровождаемого сочетанием диакритического характера. Эти два представления равны для всех целей, а сопоставления строк, поддерживающие культуру в .NET Framework, будут правильно идентифицировать их как равные, либо с CurrentCulture, либо с InvariantCulture (с или без IgnoreCase). С другой стороны, порядковое сравнение будет неправильно рассматривать их как неравные.

К сожалению, switch не делает ничего, кроме порядкового сравнения. Очередное сравнение отлично подходит для некоторых видов приложений, таких как синтаксический анализ файла ASCII с жестко определенными кодами, но сравнение обычных строк не подходит для большинства других целей.

То, что я делал в прошлом, чтобы получить правильное поведение, - это просто макет моего собственного оператора switch. Есть много способов сделать это. Один из способов - создать List<T> пар строк и делегатов. Список можно искать, используя правильное сравнение строк. Когда совпадение найдено, может быть вызван связанный делегат.

Другой вариант - сделать очевидную цепочку операторов 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"; 
    } 
+4

Если я ошибаюсь, эти два варианта отличаются только для определенных культур (например, турецких), и в этом случае он не может использовать 'ToUpperInvariant()' или 'ToLowerInvariant()'? Кроме того, он не сравнивает две неизвестные строки, он сравнивает одну неизвестную строку с одной известной строкой. Таким образом, до тех пор, пока он знает, как жестко кодировать подходящее представление верхнего или нижнего регистра, блок коммутатора должен работать нормально. –

+5

@Seth Petry-Johnson - Возможно, эта оптимизация может быть выполнена, но причина, по которой параметры сравнения строк испекиваются в рамках структуры, заключается в том, что мы не все должны становиться специалистами по лингвистике для написания правильного расширяемого программного обеспечения. –

+34

OK. Я приведу пример, где это будет доступно. Предположим, что вместо «дома» у нас было (английское!) Слово «café». Это значение может быть представлено одинаково хорошо (и в равной степени) с помощью «caf \ u00E9» или «cafe \ u0301». Ординальное равенство (как в инструкции switch) с 'ToLower()' или 'ToLowerInvariant()' будет возвращать false. 'Equals' с' StringComparison.InvariantCultureIgnoreCase' вернет true. Поскольку обе последовательности выглядят одинаково при отображении, версия 'ToLower()' является неприятной ошибкой для отслеживания. Вот почему всегда лучше делать правильные сравнения строк, даже если вы не турецкий. –

59

Простой подход заключается в том, чтобы просто опустить строку перед тем, как она войдет в оператор switch, и иметь случаи ниже.

На самом деле, верхняя часть немного лучше с точки зрения чисто экстремальной наносекундной производительности, но менее естественна, чтобы смотреть на нее.

т.д .:

string s = "house"; 
switch (s.ToLower()) { 
    case "house": 
    s = "window"; 
    break; 
} 
+0

@ Ник, есть ли у вас какая-либо ссылка на причину разницы в производительности между нижним и верхним преобразованиями? Не сложно это просто любопытно. – Lazarus

+1

Да, я понимаю, что нижняя шкала - это способ, но я хочу, чтобы это игнорировалось. Есть ли способ, которым я могу переопределить оператор switch-case? – Tolsan

+6

@Lazarus - Это от CLR через C#, он был также размещен здесь в скрытом виде: http://stackoverflow.com/questions/9033/hidden-features-of-c/12137#12137 You может запустить LinqPad с несколькими миллионами итераций. –

20

В некоторых случаях было бы целесообразно использовать перечисление. Итак, сначала проанализируйте перечисление (с флагом 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; 
    } 
} 
+0

Просто примечание: Enum TryParse, похоже, доступно с Framework 4.0 и forward, FYI. http://msdn.microsoft.com/en-us/library/dd991317(v=vs.100).aspx – granadaCoder

+0

Я предпочитаю это решение, поскольку оно препятствует использованию магических строк. – user1069816

11

Одним из возможных способов будет использование словаря с игнорированием словаря с делегатом действия.

string s = null; 
var dic = new Dictionary<string, Action>(StringComparer.CurrentCultureIgnoreCase) 
{ 
    {"house", () => s = "window"}, 
    {"house2",() => s = "window2"} 
}; 

dic["HouSe"](); 
+0

Это решение должно быть выше ... – vipero07

+1

Вместо «CurrentCultureIgnoreCase», ['OrdinalIgnoreCase'] (https://msdn.microsoft.com/en-us/library/ms973919.aspx) является предпочтительным. –

1

Я надеюсь, что это помогает попытаться преобразовать всю строку в конкретный случай, либо нижний регистр или Верхний регистр и использовать строчную строку для сравнения:

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(); 
    } 
} 
6

Извини за этот новый пост на старый вопрос , но есть новый вариант для решения этой проблемы с использованием 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; 
} 

Это решение касается вопроса, указанного в ответе на @Jeffrey L Whitledge, что прецедентного нечувствительное сравнение строк - это не то же самое, что сравнение двух строчек с нижним острием.

Кстати, в феврале 2017 года в журнале Visual Studio была интересная статья, описывающая сопоставление образцов и способы ее использования в блоках case. Пожалуйста, смотрите: Pattern Matching in C# 7.0 Case Blocks

+0

Это было бы более длинным, но я бы предпочел бы «switch (houseName)», а затем сравнение было похоже на способ, которым вы это сделали, то есть 'case var name when name.Equals (« MyHouse », ...' – LewisM

+0

@LewisM - Это интересно. Можете ли вы показать рабочий пример этого? – STLDeveloper

0

Расширение ответа на @STLDeveloperA. Новый способ сделать оценку заявление без кратному, если отчетность по состоянию на C# 7, используя шаблон, соответствующий переключатель заявление, аналогично тому, как @STLDeveloper хотя этот способ переключения на переменном переключился

string houseName = "house"; // value to be tested 
string s; 
switch (houseName) 
{ 
    case var name when name.Equals("Bungalow", StringComparison.InvariantCultureIgnoreCase): 
     s = "Single glazed"; 
    break; 

    case var name when name.Equals("Church", StringComparison.InvariantCultureIgnoreCase); 
     s = "Stained glass"; 
     break; 
     ... 
    default: 
     s = "No windows (cold or dark)" 
     break; 
} 

Визуальная студия журнал имеет nice article on pattern matching case blocks, который, возможно, стоит посмотреть.

Смежные вопросы