C#프로그래밍 10 : 클래스간 형식변환, 읽기전용필드, 중첩클래스, 확장메소드

LeeWonjin·2022년 4월 30일
0

[학부]C#프로그래밍

목록 보기
10/21

클래스간 형식 변환

implicit 연산자

implicit연산자를 오버라이딩하여 클래스간 형식 변환을 지원할 수 있다. 이 연산자를 이용해서 명시적, 암시적 형변환 모두 가능하다.

static public operator 형변환결과 클래스(형변환 전 클래스){
  // 형변환 절차
}

아래 코드는 implicit연산자로 Gorani, Cat클래스 간 명시적, 암시적 형변환을 수행한다.

namespace Program
{
    public class Animal
    {
        public int CryNumber { get; init; }
        public Animal(int CryNumber)
        {
            this.CryNumber = CryNumber;
        }
    }

    public class Gorani : Animal
    {
        public Gorani(int CryNumber) : base(CryNumber) { }
        private string sound = "queeeeaaeak!";
        public override string ToString()
        {
            string s = sound;
            for (int i = 0; i < CryNumber; i++)
                s += sound;
            
            return "[Gorani] " + s;
        }

        static public implicit operator Cat(Gorani gorani)
        {
            return new Cat(gorani.CryNumber);
        }
    }

    public class Cat : Animal
    {
        public Cat(int CryNumber) : base(CryNumber) { }
        private string sound = "yaong!";
        public override string ToString()
        {
            string s = sound;
            for (int i = 1; i < CryNumber; i++)
                s += sound;

            return "[Cat] " + s;
        }

        static public implicit operator Gorani (Cat cat)
        {
            return new Gorani(cat.CryNumber);
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Gorani gorani = new Gorani(1);
            Cat cat = new Cat(3);
            Console.WriteLine(gorani.ToString()); // [Gorani] queeeeaaeak!
            Console.WriteLine(cat.ToString()); // [Cat] yaong!yaong!yaong!

            // implicit연산자로 암시적 변환(implicit)
            Gorani catTOgorani = cat;
            Cat goraniTOcat = gorani;
            Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!
            Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!

            // implicit연산자로 명시적 변환(explict)도 가능
            catTOgorani = (Gorani)cat;
            goraniTOcat = (Cat)gorani;
            Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!queeeeaaeak!
            Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!
        }
    }
}

explicit 연산자

explicit연산자를 오버라이딩하여 클래스간 명시적 형변환을 구현할 수 있다. 위 implicit연산자 코드에서 implicit키워드를 explicit으로 바꾼 것이다.

이 경우 클래스간 명시적 형변환만 가능하다. 암시적 형변환을 시도하면 컴파일 에러가 발생한다.
CS0266 암시적으로 'Program.Cat' 형식을 'Program.Gorani' 형식으로 변환할 수 없습니다. 명시적 변환이 있습니다. 캐스트가 있는지 확인하세요.

namespace Program
{
    public class Animal
    {
        public int CryNumber { get; init; }
        public Animal(int CryNumber)
        {
            this.CryNumber = CryNumber;
        }
    }

    public class Gorani : Animal
    {
        public Gorani(int CryNumber) : base(CryNumber) { }
        private string sound = "queeeeaaeak!";
        public override string ToString()
        {
            string s = sound;
            for (int i = 0; i < CryNumber; i++)
                s += sound;
            
            return "[Gorani] " + s;
        }

        static public explicit operator Cat(Gorani gorani)
        {
            return new Cat(gorani.CryNumber);
        }
    }

    public class Cat : Animal
    {
        public Cat(int CryNumber) : base(CryNumber) { }
        private string sound = "yaong!";
        public override string ToString()
        {
            string s = sound;
            for (int i = 1; i < CryNumber; i++)
                s += sound;

            return "[Cat] " + s;
        }

        static public explicit operator Gorani (Cat cat)
        {
            return new Gorani(cat.CryNumber);
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Gorani gorani = new Gorani(1);
            Cat cat = new Cat(3);
            Console.WriteLine(gorani.ToString()); // [Gorani] queeeeaaeak!queeeeaaeak!
            Console.WriteLine(cat.ToString()); // [Cat] yaong!yaong!yaong!

            // explicit연산자로는 오직 명시적 변환(explict)만 가능
            Gorani catTOgorani = (Gorani)cat;
            Cat goraniTOcat = (Cat)gorani;
            Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!queeeeaaeak!
            Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!

            // explicit연산자로 암시적 변환(implicit)
            // -----> 컴파일에러
            catTOgorani = cat;
            goraniTOcat = gorani;
        }
    }
}

읽기전용필드 readonly

readonly키워드를 이용하여 어떤 필드를 읽기전용 필드로 지정할 수 있다.
생성자에서만 초기화 가능하다.

다른 방법(아래 코드에서는 다른 메소드)으로 값을 넣으려고 하면 컴파일에러가 발생한다.

CS0191 읽기 전용 필드에는 할당할 수 없습니다. 단, 필드가 정의된 형식의 생성자 또는 초기값 전용 setter나 변수 이니셜라이저에서는 예외입니다.

class Something
{
    private readonly int a;
    private readonly string b;

    public Something(int a, string b)
    {
        this.a = a;
        this.b = b;
    }

    public void updateValue(int a, string b)
    {
        this.a = a; // 컴파일에러 CS0191
        this.b = b; // 컴파일에러 CS0191
    }
}

중첩 클래스

어떤 클래스를 클래스 내에 중첩시켜, 밖에 보이지 않도록 할 수 있다.
중첩된 클래스는 자신이 소속된 클래스 멤버가 private일지라도 접근 가능하다.

아래 코드에서는 클래스 InnerClass가 OuterClass 내부에 중첩되어있다.

namespace Program
{
    class OuterClass
    {
        List<InnerClass> list = new List<InnerClass>();

        public void Add(int value)
        {
            InnerClass inner = new InnerClass();
            inner.AddItem(this, value);
        }

        public void Get()
        {
            foreach(InnerClass item in list)
            {
                Console.Write(item.GetItem());
                Console.Write(' ');
            }
        }

        class InnerClass
        {
            int value;
            public void AddItem(OuterClass outer, int value)
            {
                this.value = value;
                outer.list.Add(this);
            }
            public int GetItem()
            {
                return value;
            }
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            OuterClass a = new OuterClass();
            a.Add(1);
            a.Add(2);
            a.Add(3);
            a.Get(); // 1 2 3
        }
    }
}

분할 클래스

partial키워드를 사용해 키워드를 여러번에 나눠 구현할 수 있다.
코드 내 다른 블록으로 나눌 수도 있고, 다른 파일에 나눌 수도 있다.

분할 클래스로 정의되어있더라도, 컴파일 될 때 하나의 클래스로 묶여진다.
클래스의 구현이 길어지는 경우 유용하게 사용 할 수 있다.

namespace Program
{
    partial class Something
    {
        public Something(int a)
        {
            this.a = a;
            Console.WriteLine("initialized  "+a.ToString());
        }
    }

    partial class Something
    {
        int a;
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Something test = new Something(5); // initialized  5
        }
    }
}

확장 메소드

기존 클래스의 기능을 확장하는 기법
상속과는 다르다.

아래와 같은 형태로 확장을 정의한다.

public static class 클래스명
{
  public static 반환형 메소드명(this 대상형식 식별자, 매개변수){
    // 메소드 수행내용
  }
}

아래 코드는 int자료형에 대해, 입력된 매개변수와의 비교 결과를 반환하는 확장메소드이다.

namespace Program
{
    public static class Something
    {
       public static string CompareWith(this int a, int b)
        {
            if (a > b)
            {
                return "bigger";
            } 
            else if (a < b)
            {
                return "smaller";
            }
            else
            {
                return "same";
            }
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine(3.CompareWith(53)); // smaller
            Console.WriteLine(3.CompareWith(-53)); // bigger
            Console.WriteLine(3.CompareWith(3)); // same
        }
    }
}
profile
노는게 제일 좋습니다.

0개의 댓글