내일배움캠프/TIL

내일배움캠프 10일차 TIL - C# 싱글톤

서보훈 2024. 9. 26. 21:00

문제상황

텍스트 RPG 팀 프로젝트에서 모든 클래스에서 사용될만한 기능을 Utility 클래스를 만들어서 이 클래스를 통해 기능을 구현할려고 하였습니다.

그런데 이 클래스의 객체를 각 클래스마다 생성하는것도 비효율적이라 생각했고, 그렇다고 static으로 구현하여 클래스명을 통해 사용하자니 Utility.Selecter() 형태로 사용하는것이 영 불편하다고 생각하였습니다.

이 클래스를 싱글톤화 시키면 인스턴스 이름을 정해서 호출할 수 있다는점이 생각나서 클래스를 싱글톤화 하자는 생각이 들었습니다.

internal class Utility
{
    /// <summary>
    /// Console.RealLine() 입력을 정수형 int 로 변환, 변환에 실패하거나 선택지를 넘은 값 입력시 -1 반환
    /// input 콘솔입력내용, numberOfOption 선택지 갯수
    /// </summary>
    /// <param name="input"></param>
    /// <param name="numberOfOption"></param>
    /// <returns></returns>
    public int Selecter(string input, int numberOfOption)
    {
        if(int.TryParse(input, out int returnInt))
        {
            //입력값 int형으로 반환
            if(returnInt >= 1 && returnInt <= numberOfOption)
            {
                return returnInt;
            }
            else
            {
                //잘못된 입력 띄운후, 1초간 대기
                Console.WriteLine("잘못된 입력입니다.");
                Thread.Sleep(1000);
                return -1;
            }
        }
        else
        {
            //잘못된 입력 띄운후, 1초간 대기
            Console.WriteLine("잘못된 입력입니다.");
            Thread.Sleep(1000);
            return -1;
        }
    }
}

어디서나 쓸 기능인 입력을 int 값으로 변환하여 이용하는 함수

그런데...유니티가 아닌 C#은 어떻게 싱글톤화 시키는지를 몰랐습니다.

 

시도해본것

무작정 유니티 방식을 사용해봤습니다.

private static Utility instance;
public static Utility Instance
{
    get { return instance; }
    private set { instance = value; }
}

그런데...유니티에선 Awake에서 인스턴스를 지정하여 컴포넌트로 붙은 스크립트가 작동되면 인스턴스를 지정하는데 C#에서는 그게 불가능합니다.

 

해결방법

프로퍼티의 get 에서 인스턴스가 없으면 자신의 객체를 만들어 인스턴스를 생성하고, 그 인스턴스를 반환합니다.

그리고 set을 지워줍니다.

그런데 이러면 다른 클래스에서 싱글톤의 객체 생성을 시도할 수도 있습니다.

private static Utility instnace = null;
public static Utility Instance
{
    get
    {
        if(instnace == null)
        {
            instnace = new Utility();
        }
        return instnace;
    }
    

private Utility() { }

해결은 간단했습니다.

생성자를 숨겨주면 됩니다.

 

이러면 다른 클래스에서 객체를 생성하지 못하며 인스턴스의 이름을 정해줌으로써 편한 이름으로 Utility 객체를 호출할 수 있게 되었습니다.

//유틸리티 클래스 예제
Utility ut = Utility.Instance;

int selectNum = ut.Selecter(Console.ReadLine(), 2);

 

알게된점

싱글톤 패턴이 단 한개의 객체만 존재하면서 모든 객체에 전역 접근을 만들어준다는 특징을 통해 이 내용을 이해할 수 있었습니다.

단 하나의 객체는 자신의 생성자를 숨김으로써 타 클래스에서 객체를 생성할 수 없게하고 인스턴스 프로퍼티의 get에서 인스턴스가 없는데 호출을 시도할경우 자기자신의 객체를 만들어서 반환해줌으로써 싱글톤이 성립됩니다.