안녕하세요 문쑹입니다 :)
오늘은 C# 기본문법 마지막 강의입니다 ㅠㅠ 이 다음 포스팅은 SQL을 포스팅 하게 될 것 같습니다!
클래스 간의 형변환
타입을 정의하는 것은 "단위(unit)"를 빈번하게 사용하는 프로그램에서 유용합니다. 예시로써, 통화(currency)단위를 생각해보면 원, 달러, 엔화가 있을 때 이를 단순히 decimal 하나로 지정한다면 중요한 금전 계산에 오류를 발생할 수 있다.
암시적으로 정한 규칙은 코드를 유지보수하는 동안 버그를 발생시킬 수 있다.
* 외국 개발자들은 코드가 버그를 쉽게 유발할 수 있는 상황을 두고 "코드에서 냄새가 난다(code smells)"라는 표현을 사용한다고 합니다!
통화를 타입으로 예시를 작성해보겠습니다.
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 class Yen : Currency
{
public Yen(decimal money) : base(money) { }
public override string ToString()
{
return Money + "Yen";
}
}
class Program
{
static void Main(string[] args)
{
Won won = new Won(1000);
Dollar dollar = new Dollar(1);
Yen yen = new Yen(13);
won = yen; //yen 과 won이 타입이 다르기 때문에 컴파일 시에 오류 발생
}
}
Won과 Yen 사이에 형변환이 가능하도록 하고 싶다면 어떻게 해야 될까? 예를 들어, 13원이 Yen 단위에 대입될 때는 1엔으로 되고 그 반대의 변환도 일정한 환율에 따라 정의될 수 있다. 이렇게 하려면 =(대입 연산자)를 정의해야 하지만 위의 코드처럼 C#에서는 허용되지 않는다. 하지만 대체 구문으로 explicit, implicit 메서드를 정의하면 가능하다!
public class Yen : Currency
{
static public implicit operator Won(Yen yen)
{
return new Won(yen.Money * 13m); // 1엔당 13원으로 가정
}
public Yen(decimal money) : base(money) { }
public override string ToString()
{
return Money + "Yen";
}
}
implicit operator를 오버로드했으므로 암시적 형변환을 할 수 있고, 암시적인 형변환이 가능하므로 명시적으로 캐스팅 연산자를 쓰는 것도 가능하다.
public class Dollar : Currency
{
static public explicit operator Won(Dollar dollar)
{
return new Won(dollar.Money * 1000m);
}
public Dollar(decimal money) : base(money) { }
public override string ToString()
{
return Money + "Dollar";
}
}
class Program
{
static void Main(string[] args)
{
Dollar dollar = new Dollar(1);
//Won won3 = dollar; //암시적(implicit) 형변환 불가능(컴파일 오류 발생)
Won won4 = (Won)dollar; //명시적(explicit) 형변환 가능
Console.WriteLine(won4);
}
explicit은 암시적 형변환은 불가능 하므로 컴파일을 할 때 오류가 발생한다. 그래서 캐스팅을 해주어야 컴파일이 가능하다.
C#의 클래스 확장
중첩클래스(nested class) 중첩클래스는 클래스 내부에 또 다른 클래스를 정의하는 것이다.
추상 클래스(abstract class) 와 추상 메서드(abstract method)
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 // 추상 클래스를 상속받는 Line 클래스
{
Point pt1, pt2;
public Line(Point pt1, Point pt2)
{
this.pt1 = pt1;
this.pt2 = pt2;
}
public override void Draw() // 추상 클래스를 구현했다면 추상 메서드를 반드시 정의해야 함
{
Console.WriteLine("Line " + pt1.ToString() + " ~ " + pt2.ToString());
}
}
class Program
{
static void Main(string[] args)
{
DrawingObject line = new Line(new Point(10, 10), new Point(20, 20));
line.Draw(); //다형성에 따라 Line.Draw가 호출됨
}
}
추상 메서드에는 접근 제한자로 private을 지정할 수 없다. 추상클래스를 new로 인스턴스화할 수 없다는 것은 이해가 된다. 왜냐하면 추상 클래스 내부에 구현 코드가 없는 메서드, 즉 추상 메서드가 있어서이다. 추상 클래스에 반드시 추상 메서드가 포함돼 있어야 하는 것은 아니지만 그래도 여전히 추상 클래스는 new로 인스턴스화할 수 없다.
델리게이트
대상이 될 메서드의 반환 타입 및 매개변수 목록과 일치하는 델리게이트 타입을 정의한다.
접근제한자 delegate 대상_메서드의_반환타입 식별자( 대상_메서드의_매개변수_목록);
간단하게 말하자면 delegate는 영어로 "대리인"이라는 뜻이 있습니다. 누군가를 대신해서 일을 해주는 사람이라는 의미이기도 합니다.
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);
}
}
델리게이트가 타입이라는 점을 잘 기억해야할 것 같습니다. 이 때문에 변수가 사용되는 곳이라면 델리게이트 또한 함께 사용됩니다.
- 메서드의 반환값으로 델리게이트를 사용할 수 있다.
- 메서드의 인자로 델리게이트를 전달할 수 있다.
- 클래스의 멤버로 델리게이트를 정의할 수 있다.
다시 한번 델리게이트가 메서드를 가리키는 것을 떠올려본다면
- 메서드의 반환값으로 메서드를 사용할 수 있다.
- 메서드의 인자로 메서드를 전달할 수 있다.
- 클래스의 멤버로 메서드를 정의할 수 있다.
위의 복잡한 코드로 MulticastDelegate라는 내부 타입을 사용한다면 간략하게 사용할 수 있다. 지금까지의 예제에서 델리게이트 인스턴스는 하나의 메서드만 가리키고 있었는데 MulticastDelegate의 이름에서 알 수 있듯이, 여러 개의 메서드를 가리키는 것도 가능하다.
class Program
{
delegate void CalcDelegate(int x, int y);
static void Add(int x, int y) { Console.WriteLine(x + y); }
static void Sub(int x, int y) { Console.WriteLine(x - y); }
static void Mul(int x, int y) { Console.WriteLine(x * y); }
static void Div(int x, int y) { Console.WriteLine(x / y); }
static void Main(string[] args)
{
CalcDelegate calc = Add;
calc += Sub;
calc += Mul;
calc += Div;
calc(10, 5);
//출력 결과 15, 5, 50, 2
}
}
위의 예제와 같이 += 연산자를 사용하여 모든 결과를 호출할 수 있다. 반대로 원하는 메서드만 빼서 호출할 수도 있다.
class Program
{
delegate void CalcDelegate(int x, int y);
static void Add(int x, int y) { Console.WriteLine(x + y); }
static void Sub(int x, int y) { Console.WriteLine(x - y); }
static void Mul(int x, int y) { Console.WriteLine(x * y); }
static void Div(int x, int y) { Console.WriteLine(x / y); }
static void Main(string[] args)
{
CalcDelegate calc = Add;
calc += Sub;
calc += Mul;
calc += Div;
Console.WriteLine("+=");
calc(10, 5); // Add, Sub, Mul, Div 메서드 모두 호출
calc -= Mul;
Console.WriteLine("-="); // 목록에서 Mul 메서드 제거
calc(10, 5); // Add, Sub, Div 메서드만 호출
}
}
이상 포스팅을 마치겠습니다 :) 감사합니다
Hasta Luego~!