C# - List<T> Sort) 복습을 위해 작성하는 글 2023-04-22

rizz·2023년 4월 22일
0

C

목록 보기
9/25

📃 문서 파헤쳐 보기 C# - List<T> Sort

📌 IComparer<T> & Comparison<T>

Sort를 알아보기에 앞서, IComparer<T>와 Comparison<T>를 먼저 알아보자.

VS에서 IComparer<T>를 찾아 들어가 보면, 아래와 같다.

    //
    // 요약:
    //     형식이 두 개체를 비교하기 위해 구현하는 메서드를 정의합니다.
    //
    // 형식 매개 변수:
    //   T:
    //     비교할 개체의 형식입니다.
    public interface IComparer<in T>
    {
        //
        // 요약:
        //     두 개체를 비교하여 한 개체가 다른 개체보다 작거나, 같거나 또는 크다는 것을 나타내는 값을 반환합니다.
        //
        // 매개 변수:
        //   x:
        //     비교할 첫 번째 개체입니다.
        //
        //   y:
        //     비교할 두 번째 개체입니다.
        //
        // 반환 값:
        //     다음 표와 같이 x 및 y의 상대 값을 나타내는 부호 있는 정수입니다. 값 의미 0보다 작음 x가 y보다 작은 경우 0 x가 y와 같습니다.
        //     0보다 큼 x가 y보다 큰 경우
        int Compare(T x, T y);
    }

이름을 보고 예상했듯, 인터페이스로 되어있고, 두 개체를 비교하기 위해 Comparer 메소드를 정의해야 하는 것으로 보인다.

첫 번째 값이 x, 두 번째 값이 y이다.

반환값 < 0이면, x < y

반환값 > 0이면, x > y

그렇다면 Comparison<T>은 어떨까? 찾아가 보면 아래와 같다.

    //
    // 요약:
    //     동일한 형식의 두 개체를 비교하는 메서드를 나타냅니다.
    //
    // 매개 변수:
    //   x:
    //     비교할 첫 번째 개체입니다.
    //
    //   y:
    //     비교할 두 번째 개체입니다.
    //
    // 형식 매개 변수:
    //   T:
    //     비교할 개체의 형식입니다.
    //
    // 반환 값:
    //     다음 표와 같이 x 및 y의 상대 값을 나타내는 부호 있는 정수입니다. 값 의미 0 보다 작은 x가 y보다 작은 경우 0 x가 y와 같습니다.
    //     0 보다 큼 x가 y보다 큰 경우
    public delegate int Comparison<in T>(T x, T y);

Comparison<T> 역시 두 개체를 비교하는 메서드를 나타낸다.

하지만 IComparer<T>과는 다르게 delegate로 선언되어 있다.

동작 방식은 위의 IComparer<T>의 Compare 메소드와 같아 보인다.

Compare의 자세항 사항은 https://learn.microsoft.com/ko-kr/dotnet/api/system.string.compare?view=net-7.0 문서를 참고해 보자.

그러면 이제 Sort 함수를 봐보자.

 

📌 List<T> Sort

VS에서 직접 Sort 함수를 찾아 들어가 보면 Sort 함수가 매개 변수에 따라 여러 종류로 오버로딩 된 것을 볼 수 있다.

함수를 하나씩 보면 설명이 적혀 있는데, 첫 번째 Sort 함수는 아래와 같이 적혀있는 것을 볼 수 있다.

 

📌 public void Sort(int index, int count, IComparer<T> comparer);의 내용

		// 요약:
        //     지정된 비교자를 사용하여 System.Collections.Generic.List`1의 요소 범위에 있는 요소를 정렬합니다.
        //
        // 매개 변수:
        //   index:
        //     정렬할 범위의 0부터 시작하는 인덱스입니다.
        //
        //   count:
        //     정렬할 범위의 길이입니다.
        //
        //   comparer:
        //     요소를 비교할 때 사용할 System.Collections.Generic.IComparer`1 구현이거나, 기본 비교자 System.Collections.Generic.Comparer`1.Default를
        //     사용하려면 null입니다.

위의 글을 읽어보면,

index는 정렬을 시작하는 인덱스이고, count는 index부터 정렬할 개수를 의미한다.

세 번째 매개 변수에 위에서 보고 왔던 IComparer<T>가 나오는데, 이 자리에 comparer 메소드를 구현해야 한다.

comparer이 null이면 기본 비교자(오름차순 정렬)를 사용한다.

📌 EX)

		int[] data = { 5, 50, 30, 24, 0, 9 };
        List<int> _listData = new List<int>(data);
        
        static void Sort1(List<int> _listData)
        {
        	// 인덱스 1번부터 데이터 수의 -1 만큼
            // null이기 때문에 기본 비교자(오름차순 정렬)를 사용하여 정렬됨
            _listData.Sort(1, _listData.Count - 1, null);

            foreach (var data in _listData)
            {
                Console.WriteLine("data : " + data);
            }
        }
        
// 1번 인덱스부터 정렬했기 때문에 첫 번째 요소를 제외한 모든 요소가 오름차순 정렬된 모습.
// 문자 또는 문자열 형식을 기본 비교자를 사용해 정렬하면 사전 순으로 정렬된다.
Output:
data : 5
data : 0
data : 9
data : 24
data : 30
data : 50

- index 또는 count가 0보다 작으면 System.ArgumentOutOfRangeException이 발생한다.

- index 또는 count가 올바를 범위를 지정하지 않으면 System.ArgumentException이 발생한다.

- comparer이 null 일 때, 기본 비교자를 사용하게 되는데, 기본 비교자의 <T>에 적합한 속성이 아니라면 System.InvalidOperationException이 발생한다.

그럼 이제 두 번째 Sort 함수를 봐보자.

 

📌 public void Sort(Comparison<T> comparison);의 내용

        // 요약:
        //     지정된 System.Comparison`1을 사용하여 전체 System.Collections.Generic.List`1의 요소를 정렬합니다.
        //
        // 매개 변수:
        //   comparison:
        //     요소를 비교할 때 사용할 System.Comparison`1입니다.
        //
        // 예외:
        //   T:System.ArgumentNullException:
        //     comparison가 null인 경우
        //
        //   T:System.ArgumentException:
        //     comparison의 구현으로 인해 정렬 중에 오류가 발생했습니다. 예를 들어 항목을 자기 자신과 비교할 때 comparison에서 0을
        //     반환하지 않을 수 있습니다.

매개 변수는 위에서 보고 왔던 delegate로 선언되어 있는 Comparison<T> 형식이다.

직접 하나 이상의 comparison을 구현해 주어야 한다.

구현한 comparison을 사용하여 정렬을 한다.

comparison이 null일 경우 System.ArgumentNullException이 발생한다.

📌 EX)

            _listData.Sort(delegate (CStudent a, CStudent b)
            {
                if (a.ID > b.ID)
                {
                    return 1;
                }
                else if (a.ID < b.ID)
                {
                    return -1;
                }
                return 0;
            });
            // Student 객체의 ID를 기준으로 오름차순 정렬
            // 위 코드와 같이, 하나 이상의 IComparable를 구현해 주어야 한다.

다음 세 번째 함수를 봐보자.

 

📌 public void Sort();의 내용

        // 요약:
        //     기본 비교자를 사용하여 전체 System.Collections.Generic.List`1의 요소를 정렬합니다.
        //
        // 예외:
        //   T:System.InvalidOperationException:
        //     기본 비교자 System.Collections.Generic.Comparer`1.Default가 System.IComparable`1 제네릭
        //     인터페이스 또는 형식 T에 대한 System.IComparable 인터페이스 구현을 찾을 수 없습니다.
        public void Sort();

Sort에 아무런 매개 변수를 담지 않는다면 실행되는 함수이다.

기본 비교자를 사용해 리스트의 요소를 정렬하게 된다.

위에서 언급했듯ㅡ 기본 비교자의 <T> 형식과 맞지 않다면 System.InvalidOperationException이 발생한다.

다음 네 번째 함수를 봐보자.

 

📌 public void Sort(IComparer<T> comparer);의 내용

        // 요약:
        //     지정된 비교자를 사용하여 전체 System.Collections.Generic.List`1에 있는 요소를 정렬합니다.
        //
        // 매개 변수:
        //   comparer:
        //     요소를 비교할 때 사용할 System.Collections.Generic.IComparer`1 구현이거나, 기본 비교자 System.Collections.Generic.Comparer`1.Default를
        //     사용하려면 null입니다.

첫 번째 보았던 함수에서 index와 count가 매개 변수로 없으면 실행되는 함수이다.

 

⛳ 마치며...

살펴본 .Net에서 제공하는 List 정렬 함수는 총 네 가지였다. comparer을 직접 구현하여 정렬하거나, delegate를 구현하여 정렬하거나, 또는 .Net에서 제공하는 기본 비교자를 이용하여 정렬하는 등의 방법이었다. 사실 첫 번째 함수의 동작만 잘 알고 있어도 나머지 함수도 자연스럽게 알 수 있을 것 같다.

profile
복습하기 위해 쓰는 글

0개의 댓글