[TIL] 20일 차 - 제네릭과 확장메서드

ChangBeom·2025년 2월 21일

TIL

목록 보기
21/53
post-thumbnail

[1. 제네릭]

<제네릭이란?>

제네릭은 객체지향 프로그래밍에서 사용하는 기법으로, 데이터 타입을 일반화하여 코드의 재사용성을 높이고, 타입 안전성을 제공하는 기법이다. 제네릭을 사용하면 다양한 데이터 타입에 대해 동일한 코드를 작성할 수 있다.

문장만으론 이해하기 힘드니 이해하기 힘드니 쉽게 비유해서 설명하면 제네릭을 "상자" 라고 생각하면 된다.

어떤 물건이든 담을 수 있도록 만들어진 상자가 있다고 생각해보자. 이 상자에는 물건의 종류에 상관 없이 컴퓨터, 핸드폰, 옷, 책 등 아무 물건이나 넣을 수 있다. 그렇다는 것은 이 상자를 여러 컴퓨터를 보관하는 데 사용해도 되고, 여러 핸드폰을 보관하는 데 사용해도 된다는 것이다.

제네릭은 이 상자 역할을 한다. 특정 데이터 타입에 국한되지 않고, 다양한 타입의 데이터를 처리할 수 있기 때문이다.

<특징>

  • 타입 매개변수

    제네릭 클래스나 메서드는 하나 이상의 타입 매개변수를 가질 수 있다. 이 타입 매개변수는 실제 타입이 사용될 때 구체화된다.

  • 타입 안전성

    제네릭을 사용하면 컴파일 타임에 타입 검사를 수행하므로, 런타임 오류를 줄일 수 있다. 예를 들어, 제네릭 컬렉션을 사용하면 잘못된 타입의 객체가 추가되는 것을 방지할 수 있다.

  • 코드 재사용성

    제네릭은 다양한 데이터 타입에 대해 동일한 로직을 수행할 수 있으므로, 코드 중복을 줄이고 유지보수를 용이하게 한다.

특징을 보고 그럼 이 제네릭은 언제 사용하는가? 라고 생각하면 다음과 같은 경우에 사용된다.

  • 성능 향상

    제네릭을 사용하면 박싱(Boxing)과 언박싱(Unboxing) 과정을 피할 수 있다.

  • 제네릭 컬렉션

    리스트, 딕셔너리, 큐, 스택 과 같은 여러 컬렉션 클래스에서 제네릭을 사용하여 다양한 타입의 데이터를 저장하고 관리할 수 있다.

<제네릭 컬렉션>

우리가 가장 많이 사용하게 될 제네릭은 아마 컬렉션 클래스일 것이다. 위에서 잠시 언급했듯이 컬렉션은 다양한 타입의 데이터를 저장하고 관리하는 데 사용한다.

이 컬렉션 클래스를 많이 사용하는 이유는 데이터를 효율적으로 저장, 검색, 조작할 수 있는 다양한 기능을 제공하기 때문이다. 주요한 컬렉션 클래스들은 .NET 프레임워크의 일부로 제공되며, System.Collections 및 System.Collections.Ganeric 네임스페이스에 정의되어 있다.

이제 이 제네릭 컬렉션을 맛있게(?) 사용할 수 있는 기능들을 알아보자.

Linq(Language Integrated Query)

린큐, 언어 통합 쿼리라고 불리며 .NET 환경에서 데이터 쿼리를 작성할 수 있도록 지원하는 기능이다. SQL과 유사한 구문을 사용하여 데이터를 필터링, 정렬, 그룹화 및 변환할 수 있다.

<예시> : Linq를 활용하여 list에서 x==1인 x를 반환

var result = from x in list where x == 1 select x;

Linq는 다양한 데이터 소스에 대해 동일한 쿼리 구문을 사용할 수 있고, 컴파일 타임에 쿼리의 유효성 검사를 할 수 있어 런타임 오류를 줄일 수 있으며, 직관적인 구문을 사용해 코드의 가독성이 높다는 장점이 있다.

그런데 우리가 집중해야할 부분은 Linq를 사용해서 Quary문을 짜는 것이 아니다.

바로 System.Linq에서 지원하는 활용도 높은 함수들이 있다는 것이다.

아래 예시는 제네릭 컬렉션을 사용할 때 자주 사용하는 함수들이다. 물론 Linq에서 지원하는 것도 포함되어 있다.

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class Generic : MonoBehaviour
{
    List<int> list = new(){ 7,1,5,4,3 };
    List<ItemInstance> items = new();
   
    private void Start()
    {
        list.FindAll(x => x == 1);

        var result = from x in list where x == 1 select x;
  		//	바로 위 코드와 동일한 기능을 수행하는 함수
        list.Where(x => x == 1);
     
 		//	list의 첫번째 요소 반환
        list.First();
 
  		//	list의 마지막 요소 반환
        list.Last();
 
		//	list의 요소들 중 최대값 반환  
        list.Max();
 		
  		//	list의 요소들 중 최소값 반환
        list.Min();

  		//	list의 모든 요소들을 합한 값을 반환
        list.Sum();

  		//	ItemInstance 클래스를 관리 중인 items 리스트의 요소 중 count 값을 전부 합한 값을 반환
        items.Sum(x => x.count);

  		//	ItemInstance 클래스를 관리 중인 items 리스트의 요소 중 itemID가 1001인 것의 개수를 반환
        items.Count(x => x.itemId == 1001);

  		//	list를 오름차순으로 정렬
        list.Sort();

  		//	리스트 뒤집기 (Sort후 Reverse는 내림차순 정렬)
        list.Reverse();

        // Linq에서 지원하는 함수로 list를 오름차순으로 정렬
        list.OrderBy(x => x);

  		//	Linq에서 지원하는 함수로 list를 내림차순으로 정렬
        list.OrderByDescending(x => x);
       
        // 반복문으로 리스트 순회
        for (int i = 0; i < list.Count ; i++)
        {
            int j =  list[i];
        }

		// 반복문으로 딕셔너리 순회 (KeyValuePair<int,int> 대신 var 키워드를 사용해도 된다.)
        foreach (KeyValuePair<int, int> item in dictionary)
        {
            Debug.Log($"key = {item.Key}, value = {item.Value}");
        }
    }
}

[2. 확장 메서드]

<확장 메서드란?>

C#에서 기존 클래스나 구조체에 새로운 메소드를 추가할 수 있는 기능이다. 이를 통해 기존 타입의 기능을 확장할 수 있지만, 실제로 해당 타입을 수정하지 않고도 새로운 메서드를 사용할 수 있다.

확장 메서드를 사용하려면 다음과 같은 규칙을 따라야 한다.

  • 정적 클래스 안에 정의되어야 한다.
  • 정적 메서드로 정의되어야 하며, 첫 번째 매개변수는 this 키워드로 시작해야한다. 이 매개변수는 확장할 타입을 나타낸다.

아래는 확장메서드 사용 예시이다.

<예시> : Vector2 클래스에서 X값을 변환 시키는 메서드 확장

public static Vector2 SetX(this Vector2 vector, float x)
{
	return new Vector2(x, vector.y);
}

이처럼 확장 메서드를 사용하면 기존 타입에 새로운 기능을 추가할 수 있어 코드의 재사용성과 가독성을 높일 수 있다.

확장 메서드를 사용할 때 마다 Utility같은 정적 클래스에 쌓아두면 추후에 다시 사용할 일이 생길 때 편하니 미리미리 Utility 클래스를 만들어 쌓아두자.

0개의 댓글