[c#] C # Switch 문에서 IgnoreCase를 사용하는 방법

스위치의 객체가 문자열 인 switch-case 문이있는 경우 ignoreCase 비교를 수행 할 수 있습니까?

예를 들면 다음과 같습니다.

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

s값 “창”을 얻을? ignoreCase를 사용하여 문자열을 비교하도록 switch-case 문을 어떻게 재정의합니까?



답변

아시다시피 두 문자열을 소문자로 비교하는 것은 대소 문자를 무시하는 비교와 동일하지 않습니다. 이에 대한 많은 이유가 있습니다. 예를 들어 유니 코드 표준에서는 분음 부호가있는 텍스트를 여러 방법으로 인코딩 할 수 있습니다. 일부 문자에는 단일 코드 포인트에 기본 문자와 분음 부호가 모두 포함됩니다. 이러한 문자는 결합 분음 부호 문자가 뒤 따르는 기본 문자로 표시 될 수도 있습니다. 이 두 표현은 모든 목적에서 동일하며 .NET Framework의 문화 인식 문자열 비교는 CurrentCulture 또는 InvariantCulture (IgnoreCase 포함 또는 제외)를 사용하여 동일한 것으로 올바르게 식별합니다. 반면에 서수 비교는 그것들을 동일하지 않은 것으로 잘못 간주합니다.

불행히도 switch서수 비교 만 수행합니다. 서수 비교는 엄격하게 정의 된 코드로 ASCII 파일을 구문 분석하는 것과 같은 특정 종류의 응용 프로그램에 적합하지만 대부분의 다른 용도에서는 서수 문자열 비교가 잘못되었습니다.

올바른 동작을 얻기 위해 과거에 한 일은 내 자신의 switch 문을 모의하는 것입니다. 이를 수행하는 방법에는 여러 가지가 있습니다. 한 가지 방법은 List<T>케이스 문자열과 델리게이트 쌍 을 만드는 것 입니다. 적절한 문자열 비교를 사용하여 목록을 검색 할 수 있습니다. 일치하는 항목이 발견되면 연결된 대리자가 호출 될 수 있습니다.

또 다른 옵션은 명백한 일련의 if명령문 을 수행하는 것입니다 . 구조가 매우 규칙적이기 때문에 이것은 일반적으로 들리는 것만 큼 나쁘지 않은 것으로 밝혀졌습니다.

이것에 대한 가장 좋은 점은 문자열과 비교할 때 자신의 스위치 기능을 조롱 할 때 실제로 성능 저하가 없다는 것입니다. 시스템은 정수로 할 수있는 방식으로 O (1) 점프 테이블을 만들지 않을 것이므로 어쨌든 한 번에 하나씩 각 문자열을 비교할 것입니다.

비교할 사례가 많고 성능이 문제인 경우 List<T>위에서 설명한 옵션을 정렬 된 사전 또는 해시 테이블로 바꿀 수 있습니다. 그러면 성능이 스위치 문 옵션과 잠재적으로 일치하거나 초과 할 수 있습니다.

다음은 대리인 목록의 예입니다.

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";
    }


답변

더 간단한 방법은 switch 문으로 들어가기 전에 문자열을 소문자로하고 case를 낮추는 것입니다.

실제로, 어퍼는 순수한 극한의 나노초 성능 관점에서 약간 더 좋지만보기에는 덜 자연 스럽습니다.

예 :

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


답변

이전 질문에 대한이 새 게시물에 대해 죄송하지만 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 년 2 월 Visual Studio Magazine에서 패턴 매칭과 케이스 블록에서 어떻게 사용할 수 있는지에 대해 설명하는 흥미로운 기사가있었습니다. 보세요 : 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;
}


답변

어떤 경우에는 열거 형을 사용하는 것이 좋습니다. 따라서 먼저 enum을 구문 분석하고 (ignoreCase 플래그가 true 인 경우) 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;
   }
}


답변

@STLDeveloperA의 답변에 대한 확장입니다. C # 7부터 여러 if 문없이 문 평가를 수행하는 새로운 방법은이 방식으로 전환되는 변수를 전환하지만 @STLDeveloper와 유사한 방식으로 패턴 일치 Switch 문을 사용하는 것입니다.

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;
}

비주얼 스튜디오 매거진에는 살펴볼 가치가있는 패턴 매칭 케이스 블록에 대한 멋진 기사 가 있습니다.


답변

한 가지 가능한 방법은 액션 델리게이트와 함께 케이스 무시 사전을 사용하는 것입니다.

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

dic["HouSe"]();

// 호출은 텍스트를 반환하지 않고 지역 변수 만 채 웁니다.
// 당신은 실제 텍스트를 반환 교체 할 경우 ActionFunc<string>같은 것으로 사전과 가치() => "window2"


답변

다음은 클래스에서 @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 문에서 얻는 느낌과 거의 비슷합니다).

내부적으로 Dictionary를 사용하기 때문에 O (1) 동작을 얻고 문자열 목록을 살펴 보는 데 의존하지 않습니다. 물론 사전을 구성해야하며 비용이 더 많이 듭니다.

bool ContainsCase(string aCase)단순히 사전의 ContainsKey메서드 를 호출 하는 간단한 메서드 를 추가하는 것이 합리적 일 것입니다 .