Deff_Dev
[Unity/C#] 패턴 매칭 본문
패턴 매칭이란 ?
- switch문, switch 식, is 연산자를 이용하여 어떤 식이 특정 패턴(형태)과 일치하는지 검사하는 것이다.
- 패턴 매칭을 이용하면 장황하고 거추장스러운 분기문을 간결하고 읽기 쉬운 코드로 대체할 수 있다.
식 (Expression) → 패턴 매칭 → 결과 (bool)
식(Expression) 이란 ?
- 코드에서 단일 결과값을 만들어낼 수 있는 연산자와 연산자의 조합을 한다.
// 식
a = 123;
c = typeof(int);
d = a + 52;
b = int; // int는 값이 아니므로 식 X
선언 패턴
- 주어진 식이 특정 형식 ( int, string … )과 일치하는지를 평가한다.
- 만약 주어진 식과 형식이 일치한다면, 선언 패턴은 식을 해당 형식으로 변환한다.
식이 주어진 형식과 일치하는지 테스트 → 테스트가 성공하면 식을 주어진 형식으로 변환
is 연산자를 이용한 예제
private void Start()
{
object foo = 23;
// foo가 int 형식인 경우 foo를 int 형식으로 변환하여 bar에 할당한다.
if(foo is int bar)
{
Debug.Log(bar);
}
}
foo가 int 형식 일 경우 bar 변수가 if 블록 안에 생성되고 23이 출력되지만, int 형식이 아닐 경우 if 문은 실행되지않는다. (bar 변수 생성 X)
형식 패턴
- 선언 패턴과 거의 같은 방식으로 동작하지만, 변수 생성 없이 형식 일치 여부만 확인한다.
is 연산자를 이용한 예제
private void Start()
{
object foo = 23;
// foo가 int 형식인 경우 foo 출력
if(foo is int)
{
Debug.Log(foo);
}
}
switch 식을 이용한 예제
using UnityEngine;
using System;
public class Junior{}
public class Senior{}
public class Leader{}
public class Master{}
public class TestManager : MonoBehaviour
{
private int CalculateFee(object visitor)
{
return visitor switch
{
Junior => 100,
Senior => 500,
Leader => 1000,
_ => throw new ArgumentException(
$"{visitor.GetType()} 직급은 없습니다 !! ")
};
}
private void Start()
{
Debug.Log($"주니어 직급의 임금은 {CalculateFee(new Junior())}");
Debug.Log($"시니어 직급의 임금은 {CalculateFee(new Senior())}");
Debug.Log($"리더 직급의 임금은 {CalculateFee(new Leader())}");
Debug.Log($"마스터 직급의 임금은 {CalculateFee(new Master())}");
}
}
상수 패턴
- 식이 특정 상수와 일치하는지를 검사한다.
- 정수, 문자열, null, enum 등 모든 상수와 매칭할 수 있다.
문자열에 대한 상수 패턴 매칭 예제
using UnityEngine;
using System;
public class TestManager : MonoBehaviour
{
private int GetCountryCode(string nation)
{
switch (nation)
{
case "KR":
return 82;
case "US":
return 1;
case "UK":
return 44;
default:
throw new ArgumentException($"{nation} 국가 코드는 존재하지 않습니다.");
}
}
private void Start()
{
Debug.Log($"{GetCountryCode("KR")}");
Debug.Log($"{GetCountryCode("US")}");
Debug.Log($"{GetCountryCode("UK")}");
Debug.Log($"{GetCountryCode("CN")}");
}
}
프로퍼티 패턴
- 식의 속성이나 필드가 패턴과 일치하는지를 검사한다.
- 입력된 식이 int, double 같은 기본 데이터 형식이 아닌 경우에 유용하게 사용할 수 있다.
using UnityEngine;
public class Car
{
public string Model { get; set; }
public int Year { get; set; }
}
public class TestManager : MonoBehaviour
{
private string GetNickname(Car car)
{
if (car is Car { Model: "Mustang", Year: 1967 })
{
return "Fastback";
}
else
{
return "Unknown";
}
}
private void Start()
{
Debug.Log(GetNickname(new Car() {Model ="Mustang", Year = 1967}));
Debug.Log(GetNickname(new Car() {Model ="Mustang", Year = 1999}));
}
}
관계 패턴
- , >=, ==, !=, <, <=와 같은 관계 연산자를 이용하여 입력받은 식을 상수와 비교한다.
private void Start()
{
int score = 60;
bool isPass = score switch
{
< 60 and > 0 => false,
< 100 and >= 60 => true,
_ => false
} ;
Debug.Log(isPass);
}
논리 패턴
- 패턴과 패턴을 패턴 논리 연산자 (and, or, not)을 조합해서 하나의 논리 패턴으로 만들 수 있다.
using UnityEngine;
public class OrderItem
{
public int Amount { get; set; }
public int Price { get; set; }
}
public class TestManager : MonoBehaviour
{
float GetPrice(OrderItem orderItem) => orderItem switch
{
OrderItem { Amount: 0 } or OrderItem { Price: 0 }
=> 0.0f,
OrderItem {Amount: >= 100} and OrderItem {Price: >= 10000}
=> orderItem.Amount * orderItem.Price * 0.8f,
not OrderItem {Amount: < 100} // not은 조건의 반대의 경우 -> 수량이 100 이상이고 price가 10000미만일 때
=> orderItem.Amount * orderItem.Price * 0.9f,
_ => orderItem.Amount * orderItem.Price,
};
private void Start()
{
Debug.Log(GetPrice(new OrderItem() {Amount = 0, Price = 10000}));
Debug.Log(GetPrice(new OrderItem() {Amount = 100, Price = 10000}));
Debug.Log(GetPrice(new OrderItem() {Amount = 100, Price = 9000}));
Debug.Log(GetPrice(new OrderItem() {Amount = 1, Price = 1000}));
}
}
괄호 패턴
- 소괄호 ()로 패턴을 감싼다.
- 보통 논리 패턴으로 여러 패턴을 조합한 뒤 이를 새로운 패턴으로 만드는 경우에 사용한다.
private void Start()
{
object age = 30;
if (age is (int and > 19))
{
Debug.Log("성인");
}
}
위치 패턴
- 식의 결과를 분해하고, 분해된 값들이 내장된 복수의 패턴과 일치하는지를 검사한다.
- 위치 패턴 안에 내장되는 패턴에는 형식 패턴, 상수 패턴 등 어떤 패턴이든 올 수 있다.
- 분해된 값들과 내장된 패턴의 개수, 순서가 일치해야 한다는 점을 주의해야 한다.
using UnityEngine;
public class Audience
{
public bool IsCitizen { get; set; }
public int Age { get; set; }
public Audience(bool isCitizen, int age)
{
IsCitizen = isCitizen;
Age = age;
}
// Deconstruct() : 값을 반환할 때 쓰는 함수
public void Deconstruct(out bool isCitizen, out int age)
{
isCitizen = IsCitizen;
age = Age;
}
}
public class TestManager : MonoBehaviour
{
int CalculateFee(Audience audience) => audience switch
{
(true, < 19) => 100,
(true, _) => 200, // 내국인 20살 이상
(false, < 19) => 200,
(false, _) => 400, // 외국인 20살 이상
};
private void Start()
{
Audience a1 = new Audience(true, 10);
Debug.Log($"내국인 : {a1.IsCitizen}, 나이 : {a1.Age}, 요금 : {CalculateFee(a1)}");
// Deconstruct() 호출 후 a1의 IsCitizen, Age 값을 튜플 변수에 저장
var (isCitizen, age) = a1;
Debug.Log($"내국인 : {isCitizen}, 나이 : {age}");
Audience a2 = new Audience(false, 33);
Debug.Log($"내국인 : {a2.IsCitizen}, 나이 : {a2.Age}, 요금 : {CalculateFee(a2)}");
}
}
여기서 Deconstruct 메소드는 C# 7.0부터 도입된 새로운 함수로, 메서드를 클래스 내에 정의하면 해당 클래스의 인스턴스를 분해하여 여러 개의 값을 한 번에 out 매개변수로 통해 반환할 수 있는 함수이다.
튜플과 함께 사용되며 객체의 다양한 인스턴스 값을 간편하게 추출하는 데 유용하다.
var 패턴
- null을 포함한 모든 식의 패턴 매칭을 성공시키고, 그 식의 결과를 변수에 할당한다.
- 어떤 식의 결과를 임시 변수에 할당한 뒤 추가적인 연산을 수행하고자 할 때 유용하게 사용할 수 있다.
private void Start()
{
// Player 태그를 가진 GameObject를 var형 임시 변수에 할당
var player = GameObject.FindGameObjectWithTag("Player");
if (player != null) // GameObject를 찾았을 때
{
// Player GameObject를 찾았을 때 실행됨
player.GetComponent<Player>().Move();
}
}
무시 패턴
- var 패턴처럼 모든 식과의 패턴 일치 검사를 성공시키지만 var 패턴과 다르게 is 식에서는 사용할 수 없고, switch 식에서만 사용할 수 있다.
- ‘모든 식’을 매칭할 수 있기 때문에 switch 문의 default 케이스와 비슷한 용도로 사용한다.
using UnityEngine;
using System;
public class TestManager : MonoBehaviour
{
private int GetCountryCode(string nation)
{
int code = nation switch
{
"KR" => 82,
"US" => 1,
"UK" => 44,
// 무시 패턴 매칭
_ => throw new ArgumentException($"{nation} 국가 코드는 존재하지 않습니다.")
};
return code;
}
private void Start()
{
Debug.Log($"{GetCountryCode("KR")}");
Debug.Log($"{GetCountryCode("US")}");
Debug.Log($"{GetCountryCode("UK")}");
Debug.Log($"{GetCountryCode("CN")}");
}
}
그 이외에 목록 패턴도 존재하지만 유니티는 C# 8.0까지 지원하기 때문에 list patterns (C# 11.0)를 지원하지않는다.
스스로 공부하면서 정리한 내용으로 틀린 내용이 있을 수도 있습니다. 고쳐야할 점이 있거나 이해가 안되는 부분이 있으시다면 댓글 달아주세요 !
'Unity(유니티) > 유니티 공부' 카테고리의 다른 글
[Unity C#] Trigger와 Action을 이용한 NPC 대화 구현 (0) | 2024.05.12 |
---|---|
[Unity/C#] 프로퍼티를 왜 쓰는가 ? (0) | 2024.04.23 |
[Unity/C#] switch 식 (0) | 2024.04.10 |
[Unity/C#] null 조건부 연산자 (?.) (0) | 2024.04.10 |
[Unity/C#] Unity Ads 문제와 해결 방법 (0) | 2024.03.26 |