본문으로 바로가기
반응형

클래스 간의 형변환

C#은 자료형이 값과 참조형으로 나뉜다

 

값은 기본연산자를 지원해준다.

int A = 3;

int B = 4;

A + B = 7을 굳이 정의해주지 않아도 컴파일러가 알아서 연산을 해준다.

 

참조형은 프로그래머가 디자인한 것이다.

class T(타입)

{

}

T A = new T();

T B = new T();

A + B = ??????         // 컴파일러가 객체 끼리의 연산을 인식하지 못함

 

 그렇기 때문에 객체 끼리 덧셈하려면 직접 참조형을 만들어서 연산을 만들어야한다.

 책과 책을 더한다라고할때 원래라면 없지만 그 안의 기능을 내가 어떻게 정의할지 생각해볼 수 있다(가격을 더할꺼냐 이름을 더할꺼냐 저자이름을 어떻게할꺼냐 등등)

 

 

(operator 연산자를 사용)

public static T operator + (T A, T B)

{

//더할때 뭐할지에 대한 구문

// 이 메서드안에 기능은 내가 마음대로 짤 수 있다.

return ;

}

public class Currency
{
	decimal money;
	public decimal Money
	{ 
		get { return money; } 
	}

	public Currency(decimal money)
	{
		this.money = money;
	}
}

public class Won : Currency
{
	public Won(decimal money) : base(money)
 	{
	}

	public override string ToString()
	{
		return Money + "Won";
	}
}

public class Dollar : Currency
{
	public Dollar(decimal money) : base(money)
	{
	}

	public override string ToString()
	{
		return Money + "Dollar";
	}

	public static explicit operator Won(Dollar dollar)
	{
		return new Won(dollar.Money * 1000m);
	}
}

public class Yen : Currency
{
	public Yen(decimal money) : base(money)
	{
	}

	public override string ToString()
	{
		return Money + "Yen";
	}

	public static implicit operator Won(Yen yen)
	{
		return new Won(yen.Money * 13m);
	}
}

class Program
{
	static void Main(string[] args)
	{
		Won won = new Won(1000);
		Dollar dollar = new Dollar(1);
		Yen yen = new Yen(13);

		Console.WriteLine(won);
		Console.WriteLine(dollar);
		Console.WriteLine(yen);

		won = yen;
		//won = (Won)yen;
		Console.WriteLine(won);

		// won = dollar; // 오류
		won = (Won)dollar;
		Console.WriteLine(won);
	}
}

override된것과 implict operator구문에 주목해보자

클래스 간의 형 변환은 explicit, implicit 메서드를 정의하는 것으로 형변환을 할 수 있다. Yen에서 Won으로 대입하고싶으면 implicit 연산자를 사용해 위의 예제처럼 사용할 수 있다.implicit operator를 오버로드 한다면 암시적 형 변환을 할 수 있고, 암시적 형 변환이 가능하다면 명시적으로 캐스팅 연산자를 쓰는 것도 허용된다. 하지만 개발자가 의도한 형 변환만 가능하도록 제한을 걸고 싶다면 implicit 대신 explicit 연산자를 제공해야 한다.

 

 

 

 

중첩클래스

중첩 클래스는 클래스 내부에 또 다른 클래스를 정의하는 것이다. 특정 클래스는 다른 구성 요소에 재사용되기보다는 일정한 클래스 전용으로 내장되어 사용되기도 한다. 하지만 클래스를 나누어 정의하면 다른 클래스에서 사용되어 실수할 여지를 남길 수 있다. 따라서 중첩 클래스를 사용하여 클래스 내부로 정의를 제한하는 것이 좋다. 중첩 클래스는 접근 제한자가 생략되면 다른 멤버와 마찬가지로 private이 지정되어 외부에서 인스턴스를 직접 생성하는 것이 불가능해진다. 중첩 클래스를 외부에서 사용하고 싶다면 명시적으로 public 접근 제한자를 지정해야 한다.

 

 

추상 클래스

 전에 설명한 메서드 오버라이드는 virtual 메서드를 정의한 부모 클래스에서 그에 대한 기본적인 기능을 구현하고, 자식 클래스에서는 override 예약어를 이용해 그 기능을 재정의했다. 또, 한 부모 클래스와 자식 클래스 모두 new를 이용해 인스턴스를 생성하는 것이 가능하다.

 

 그런데, 때로는 부모 클래스의 인스턴스를 생성하지 못하게 하면서 특정 메서드에 대해 자식 클래스가 반드시 재정의하도록 강제하고 싶은 경우가 존재한다. 이때 추상 클래스(abstract class)추상 메서드(abstract method)를 사용한다. 

 

 추상 메서드는 abstract 예약어가 지정되고 구현 코드가 없는 메서드를 말한다. 추상 메서드는 일반 클래스에 존재할 수 없으며, 반드시 추상 클래스 안에서만 선언할 수 있다. 또, 추상 메서드에는 접근 제한자로 private을 지정할 수 없다. 추상 클래스와 일반 클래스의 차별점은 new를 사용해 인스턴스로 만들 수 없다는 것과 추성 메서드를 가질 수 있다는 것이다. 추상 클래스를 상속받은 자식 클래스가 추상 메서드를 오버라이딩 안 할 경우 자식 클래스도 추상 클래스가 된다.

 

class Point
{
	int x, y;

	public Point(int x, int y)
	{
		this.x = x; this.y = y;
	}

	public override string ToString()
	{
		return "X: " + x + ", Y: " + y;
	}
}

abstract class DrawingObject
{
	public abstract void Draw();

	public void Move()
	{
		Console.WriteLine("Move");
	}
}

class Line : DrawingObject
{
	Point pt1, pt2;
	public Line(Point pt1, Point pt2)
	{
		this.pt1 = pt1;
		this.pt2 = pt2;
	}

	public override void Draw()
	{
		Console.WriteLine("Line " + pt1+ " ~ " + pt2);
	}
}

class Program
{
	static void Main(string[] args)
	{
		DrawingObject line = new Line(new Point(10, 10), new Point(20, 20));
		line.Draw();
	}
}

추상 클래스에서 예시를 들면,

건물을 지으려면 설계도면이 있어야하고 설계도면은 설계 가이드를 통해 만들어진다.

- 설계가이드 : class의 설계를 가이드해줌(추상적인 말로 되어있다)

- 설계도면  :  class(구체적으로 만들어놓았음)

- 건물  :  객체(실제로 만든 것)

 

 

이때, 설계가이드를 추상클래스라고 볼 수 있다. -> 추상 클래스는 가이드역할을 하는 클래스이다.

가이드만으로는 건물을 지을수 없다 -> 추상클래스로만 객체를 생성할 수 없다

특정 추상 클래스가 상속되어있다는건 이 기능이 구현되어있다라고 생각하면된다 -> 반드시 이 메서드를 만들어라라고 강제할 수 있다.

 

 

 

델리게이트

메서드에 주소를 가진애도 담을 수 있지 않을까? 그걸 전문적으로 담는 애를 델리게이트이다

메서드 참조 변수라고 생각하자.

 

delegate를 쓰면 새로운 자료형을 생성한다는 뜻이다.

(참고로 class ㅁ  { } 를하면 ㅁ라는 자료형을 만드는 것이다.)

 

접근제한자 delegate 대상_메서드의_반환타입 식별자(...대상_메서드의_매개변수_목록...);

예시) delegate int  CalcDelegate (int x, int y);

 

 

지난번에 썼던 Car예제에서 class Car에서  Car가 New Type이다.

Car Obj = new Car();

Type 변수명 = Car()라는 객체생성

 

클래스에서 썼던것처럼

CalcDelegate test;   라고 선언하면

CalcDelegate라는 타입의 test를 선언하는 것이다.

(Car라는 타입의 Cobj를 선언하는것처럼)

 

 

class는 { }구문으로 정의하지만, 델리게이트는 한줄로 정의해놨다(메서드 이름도 적을 수 있다)

메서드를 가리키는 변수를 델리게이트

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 class Program
    {
        delegate void CarDeal();
        delegate void CarDeal2(int z);
        static void Car()
        {
            Console.WriteLine("메뚜기");
        }
        static void Car2(int A)
        {
            Console.WriteLine("메뚜기2 " + A);
        }
        static void Ottogi()
        {
            Console.WriteLine("오뚜기");
        }
        
        static void Main(string[] args)
        {
            Car();
            CarDeal Dealer;
            Dealer = Car;
            Dealer();
            Dealer = Ottogi;
            Dealer();
            CarDeal2 Dealer2;
            Dealer2 = Car2;
            Dealer2(100);
        }
    }
cs

델리게이트가 타입이라는 점은 중요하다. 변수가 사용되는 곳이라면 델리게이트 또한 함께 사용될 수 있다.

1. 메서드의 반환값으로 델리게이트(메서드)를 사용할 수 있다.

2. 메서드의 인자로 델리게이트(메서드)를 전달할 수 있다.

3. 클래스의  멤버로 델리게이트(메서드)를 정의할 수 있다.

 

 

public class Mathematics
    {
        delegate int CalcDelegate(int x, int y);

        static int Add(int x, int y) { return x + y; }
        static int Sub(int x, int y) { return x - y; }
        static int Mul (int x, int y) { return x * y; }
        static int Div(int x, int y) { return x / y; }

        CalcDelegate[] method;

        public Mathematics()
        {
            //static 메서드를 가리키는 델리게이트 배열 초기화
            method = new CalcDelegate[] { Mathematics.Add, Mathematics.Sub, Mathematics.Mul, Mathematics.Div };
        }
        // methods 배열에 담긴 델리게이트를 opCode 인자에 따라 호출
        public void Calculate(char opCode, int Operand1, int Operand2)
        {
            switch(opCode)
            {
                case '+':
                    Console.WriteLine("+ : " + method[0](Operand1, Operand2));
                    break;
                case '-':
                    Console.WriteLine("- : " + method[1](Operand1, Operand2));
                    break;
                case '*':
                    Console.WriteLine("* : " + method[2](Operand1, Operand2));
                    break;
                case '/':
                    Console.WriteLine("/ : " + method[3](Operand1, Operand2));
                    break;
            }
        }
    }
    class Program
    {
        // 3개의 매개변수를 받고 void를 반환하는 델리게이트 정의
        // 매개변수의 타입이 중요할 뿐 매개변수의 이름은 임의로 정할 수 있음.

        delegate void WorkDelegate(char arg1, int arg2, int arg3);

        static void Main(string[] args)
        {
            Mathematics math = new Mathematics();
            WorkDelegate work = math.Calculate;
            work('+', 10, 5);
            work('-', 10, 5);
            work('*', 10, 5);
            work('/', 10, 5);
        }
    }

 

 

 

 

 

테스트문제)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
부모 클래스, 자식 클래스의 구조를 구현하시오.
    부모 클래스명은 Person으로 구현하시오.
    상속받는 클래스명은 Developer와 Sales로 구현하시오.
    부모 클래스의 필드 3가지를 이름(name), 나이(age), 부서(dept)로 하고 타입을 적절하게 선택하여 작성하시오.
    부모 클래스의 필드 3가지를 string형으로 리턴하는 ToString메소드를 오버라이드하여 작성하시오.
    부모 클래스에서 가상 메소드를 구현하여야 하며 "회사원 입니다."를 화면에 출력하는 기능을 부여하시오.
    Developer 클래스는 부모 클래스를 상속받으며 가상 메소드를 재정의해서 "개발자 입니다."라고 화면에 출력하게 하시오.
    Sales 클래스는 부모 클래스를 상속받으며 가상 메소드를 재정의해서 "세일즈맨 입니다."라고 화면에 출력하게 하시오.
    Main() 메소드가 포함된 Program 클래스를 구현하시오.
    Main() 메소드에는 Person 배열을 생성하고 2명의 신상 정보를 Developer, Sales 객체를 생성하며 입력하시오.
    Main() 메소드의 Person 배열을 foreach문을 사용하여 객체 정보를 출력하고 가상메소드를 호출하시오.
*/

using Microsoft.SqlServer.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace _20200604_TEST
{
    public class Person
    {
        string name;
        int age;
        string dept;

        public Person(string name, int age, string dept)
        {
            this.name = name;
            this.age = age;
            this.dept = dept;
        }
        public override string ToString()
        {
            return "성명 : " + name + "\n나이 : " + age + "\n부서 : " + dept;
        }
        virtual public void Print()
        {
            Console.WriteLine("회사원입니다.");
        }
 
    }
    public class Developer : Person
    {
        public Developer(string name, int age, string dept) : base(name, age, dept)
        {
 
        }
        override public void Print()
        {
            Console.WriteLine("개발자입니다");
        }
    }
 
    public class Sales : Person
    {
        public Sales(string name, int age, string dept) : base(name, age, dept)
        {
 
        }
        override public void Print()
        {
            Console.WriteLine("세일즈맨입니다.");
        }
    
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person[] A = new Person[] { new Developer("홍길동"27"연구소" ), new Sales("고길동",25,"판매" ) };
            foreach (Person B in A)
            {
                Console.WriteLine(B);
                B.Print();
                Console.WriteLine();
            }
        }
    }
}
 
cs

 

반응형