ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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}");
                    }
                }

    일반버전&nbsp; 모두 출력
    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
Designed by Tistory.