-
LINQ웹 서버/고급 C# 문법 2022. 4. 22. 23:13
1. LINQ의 필요성
Q) 플레이어의 레벨 기준으로 오름차순으로 정렬하는 코드.
using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; namespace CSharp { public enum ClassType { Knight, Archer, Mage } public class Player { public ClassType classType { get; set; } public int Level { get; set; } public int Hp { get; set; } public int Attack { get; set; } } class Program { static List<Player> _players = new List<Player>(); static void Main(string[] args) { Random rand = new Random(); for(int i = 0; i < 100; i++) { ClassType type = ClassType.Knight; switch(rand.Next(0, 3)) { case 0: type = ClassType.Knight; break; case 1: type = ClassType.Archer; break; case 2: type = ClassType.Mage; break; } Player player = new Player() { classType = type, Level = rand.Next(0, 100), Hp = rand.Next(100, 1000), Attack = rand.Next(5, 50) }; _players.Add(player); } List<Player> players = GetHightLevelKnights(); foreach(Player player1 in players) { Console.WriteLine($"직업 : {player1.classType}, 레벨 : {player1.Level}, Hp : {player1.Hp}, 공격력 : {player1.Attack}"); } } // Q) 레벨이 50 이상인 Knight를 출력해서 레벨기준 오름차순으로 정렬 public static List<Player> GetHightLevelKnights() { List<Player> players = new List<Player>(); int i, j; for(i = 0; i < _players.Count; i++) { if (_players[i].Level >= 50 && _players[i].classType == ClassType.Knight) players.Add(_players[i]); } int idx; Player temp; for(i = 0; i < players.Count - 1; i++) { idx = i; for(j = i + 1; j < players.Count; j++) { if (players[idx].Level > players[j].Level) idx = j; } temp = players[idx]; players[idx] = players[i]; players[i] = temp; } return players; } } }
플레이어의 모든 정보를 랜덤함수를 통해 저장하였고
Knight타입의 레벨을 오름차순으로 정렬하여 출력하는 코드이다.
그리고 위의 코드를 LINQ를 이용하여 구현할 수 있다.
2. LINQ 사용
//일반 버전 { List<Player> players = GetHightLevelKnights(); foreach (Player player1 in players) { Console.WriteLine($"직업 : {player1.classType}, 레벨 : {player1.Level}, Hp : {player1.Hp}, 공격력 : {player1.Attack}"); } }
일반적으로 위의 코드로 출력을 표현했다면 LINQ버전은 다음 코드처럼 표현할 수 있다.
//LINQ 버전 { var players = from p in _players where p.classType == ClassType.Knight && p.Level >= 50 orderby p.Level select p; Console.WriteLine(); foreach (Player player1 in players) { Console.WriteLine($"직업 : {player1.classType}, 레벨 : {player1.Level}, Hp : {player1.Hp}, 공격력 : {player1.Attack}"); } }
마치 SQL문법과 거의 흡사하다. 그래도 조금은 다른면이 있으니 살펴보자.
우선 from의 의미로 기존의 SQL에서는 테이블을 나타냈지만 C#에서 from은 아래 코드의 의미와 비슷하다.
foreach (Player p in _players)
from p에서 p는 우리가 임의로 정해준 별칭이다.
where, orderby는 SQL과 비슷하다. 각각 조건설정과 정렬기능이다.
select는 가공해서 추출하는 부분이다. 만약에 p.Hp라고 하면 Hp의 정보만 추출할 수 있다. 아래의 코드는 select를 가공해서 추출하는 코드이다.
//일반 버전 { List<Player> players = GetHightLevelKnights(); foreach (Player player1 in players) { Console.WriteLine($"직업 : {player1.classType}, 레벨 : {player1.Level}, Hp : {player1.Hp}, 공격력 : {player1.Attack}"); } } // LINQ버전 : Hp정보만 추출 { var players = from p in _players where p.classType == ClassType.Knight && p.Level >= 50 orderby p.Level select p.Hp; Console.WriteLine(); foreach (int p in players) { Console.WriteLine(p); //Console.WriteLine($"직업 : {p.classType}, 레벨 : {p.Level}, Hp : {p.Hp}, 공격력 : {p.Attack}"); } }
일반버전 모두 출력 LINQ버전 Hp만 출력 그리고 new를 이용해서 새로운 인스턴스를 생성할 수도 있다.
var players = from p in _players where p.classType == ClassType.Knight && p.Level >= 50 orderby p.Level select new { Hp = p.Hp, Level = p.Level * 2 }; // Hp는 그대로 만들고 Level은 추출한 레벨의 2배로 인스턴스 생성
다만 LINQ는 유니티에서 사용을 잘 안한다. 이유는 버그가 터진다고 한다..
그래서 웹 서버에서만 사용하는 것을 권유한다.
그래도 장점이라면 가독성이 좋고 MS제품군과 호환이 잘 된다는 점에서 무조건 알아야 하는 기능이다.
3. 중첩 from
중첩foreach문처럼 from을 중첩해서 쓸 수 있다.
public class Player { public ClassType classType { get; set; } public int Level { get; set; } public int Hp { get; set; } public int Attack { get; set; } public List<int> Items { get; set; } = new List<int>(); }
먼저 Player클래스에 List<int> Items를 추가한다.
그 다음 Main문안에서 리스트에 임의의 데이터 5개를 추가한다.
for( int j = 0; j < 5; j++) { player.Items.Add(rand.Next(1, 101)); }
마지막으로 아래와같이 from을 중첩해서 사용하면된다. 중첩foreach문처럼 생각해도 된다.
//중첩 from // ex) 모든 아이템 목록을 추출 (아이템 id < 30를 추출) { var playerItems = from p in _players from i in p.Items where i < 30 select new { p, i }; var li = playerItems.ToList(); }
4. Grouping
SQL의 GROUP BY와 HAVING의 비슷한 기능도 사용할 수 있다.
// Grouping { var playerBylevel = from p in _players group p by p.Level into g // g는 그룹의 이름 orderby g.Key select g; Console.WriteLine("\n------------Grouping 버전--------------"); foreach (var Players in playerBylevel) { int i = 1; Console.WriteLine($"\n key : {Players.Key}레벨 그룹"); foreach (var groups in Players) { Console.WriteLine($"{i++} 번째 유저의 공격력 : {groups.Attack}, Hp : {groups.Hp}"); } } }
5. Join
SQL의 Join의 사용방법은 아래와 같다.
// Join(내부 조인) // outer join(외부 조인) { // 레벨이 1, 5, 10인 데이터만 추출 List<int> levels = new List<int>() { 1, 5, 9 }; var playerLevels = from p in _players join l in levels // p와 l을 비교 on p.Level equals l // 레벨이 같은지 비교 orderby p.Level // 정렬 select p; // Player형의 데이터로 추출 }
6. LINQ 표준 연산자
// LINQ 표준 연산자 { var players = from p in _players where p.classType == ClassType.Knight && p.Level >= 50 orderby p.Level select p; var players2 = _players .Where(p => p.classType == ClassType.Knight && p.Level >= 50) .OrderBy(p => p.Level) .Select(p => p); }
두 개의 구문은 결과가 완전히 같다. 문법에 대해서만 유심히 살펴보고 표준 연산자가 조금 더 많은 기능을 표현할 수 있다는것만 알아두자.
'웹 서버 > 고급 C# 문법' 카테고리의 다른 글
Async, Await (0) 2022.04.22