1) 메서드 오버라이드(method override)
오버라이드는 시그니처가 완전히 동일한 메서드를 재정의할 때 사용한다.
ex) 부모 타입으로 암시적 형 변환이 된 경우
class Mammal
{
public void Move()
{
Console.WriteLine("이동한다.");
}
}
class Lion : Mammal
{
public void Move()
{
Console.WriteLine("네 발로 움직인다.");
}
}
class Whale : Mammal
{
public void Move()
{
Console.WriteLine("수영한다");
}
}
class Human : Mammal
{
public void Move()
{
Console.WriteLine("두 발로 움직인다");
}
}
class Program
{
static void Main(string[] args)
{
Mammal aMammal = new Mammal();
aMammal.Move();
aMammal = new Lion();
aMammal.Move();
aMammal = new Whale();
aMammal.Move();
aMammal = new Human();
aMammal.Move();
}
}
인스턴스 자체는 Lion 타입이므로 의도했던 동작이 아니다. 따라서 가상 메서드(virtual method)라는 것이 제공된다. 일반 메서드를 가상 메서드로 바꾸려면 virtual이라는 예약어를 부모 클래스 단계에서 명시하면 된다. 그리고 자식 클래스에서 해당 메서드가 다형성을 띠도록 명시적으로 override 예약어를 지정하기만 하면 된다. virual 예약어는 객체 타입에 맞게 호출시킨다.
virtual/override 예약어를 적용함으로써 부모에서 정의한 Move라는 하나의 동작에 대해 자식 클래스의 인스턴스에 따라 다양하게 재정의(override)될 수 있었고, 인스턴스가 어떤 타입으로 형 변환해도 그 특징이 유지되는 것을 볼 수 있다. 이를 메서드 오버라이드(method override)라고 한다.
때로는 자식 클래스에서 다형성 차원에서가 아닌 순수하게 독립적인 하나의 메서드로 이름을 정의하고 싶은 경우도 고려해야 한다. C#에서는 같은 이름의 메서드를 일부러 겹쳐서 정의했다는 개발자의 의도를 명시적으로 표현할 수 있게 new 예약어를 제공한다. 아무것도 표기를 안 했을 경우 new 예약어로 컴파일러가 번역한다.
ex)
class Mammal
{
public virtual void Move()
{
Console.WriteLine("이동한다.");
}
}
class Lion : Mammal
{
public override void Move()
{
Console.WriteLine("네 발로 움직인다.");
}
}
class Whale : Mammal
{
public override void Move()
{
Console.WriteLine("수영한다");
}
}
class Human : Mammal
{
public override void Move()
{
Console.WriteLine("두 발로 움직인다");
}
}
class Program
{
static void Main(string[] args)
{
Mammal aMammal = new Mammal();
aMammal.Move();
aMammal = new Lion();
aMammal.Move();
aMammal = new Whale();
aMammal.Move();
aMammal = new Human();
aMammal.Move();
}
}
Mammal[] aMammal = new Mammal[4] { new Mammal(), new Lion(), new Whale(), new Human() };
//aMammal[0] = new Mammal();
//aMammal[1] = new Lion();
//aMammal[2] = new Whale();
//aMammal[3] = new Human();
//aMammal[0].Move();
//aMammal[1].Move();
//aMammal[2].Move();
//aMammal[3].Move();
foreach(Mammal temp in aMammal)
{
temp.Move();
}
1.1) base를 이용한 메서드 재사용
부모 클래스와 자식 클래스에 중복 코드를 제거하려면 base 키워드를 사용하면 된다.
ex)
public class Computer
{
public virtual void Boot()
{
Console.WriteLine("메인보드 켜기");
}
}
public class Notebook : Computer
{
public override void Boot()
{
base.Boot(); // Console.WriteLine("메인보드 켜기");
Console.WriteLine("액정 화면 켜기");
}
}
1.2) object 기본 메서드 확장
public class Object
{
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public virtual string ToString();
// ... 생략 ...
}
일반적으로 ToString의 경우 클래스의 인스턴스 값을 적절하게 표현하는 내용으로 재정의하는 것이 보통이다. WriteLine에 객체 참조 변수를 넣으면 자동으로 ToString()이 호출된다.
ex)
class Car
{
int iSpeed; // 속도
string sColor; // 색상
string sName; // 이름
string sVender; // 제조사
public void Print()
{
Console.WriteLine(this.ToString());
}
public override string ToString()
{
return "속 도 : " + iSpeed + "\n색 상 : " + sColor + "\n이 름 : " + sName + "\n제조사 : " + sVender;
}
public Car(int iSpeed, string sColor, string sName, string sVender)
{
this.iSpeed = iSpeed;
this.sColor = sColor;
this.sName = sName;
this.sVender = sVender;
}
}
class Program
{
static void Main(string[] args)
{
Car aCar = new Car(100, "빨강", "test", "현대");
Console.WriteLine(aCar);
aCar.Print();
}
}
public 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;
}
}
class Program
{
static void Main(string[] args)
{
Point pt = new Point(5, 10);
Console.WriteLine(pt);
}
}
class Book
{
decimal isbn13;
string title;
string contents;
public Book(decimal isbn13, string title, string contents)
{
this.isbn13 = isbn13;
this.title = title;
this.contents = contents;
}
public override bool Equals(object obj)
{
if(obj == null) // 객체가 들어왔는지 체크
{
return false;
}
Book book = obj as Book;
if(book == null) // 객체가 Book 타입인지 체크
{
return false;
}
return this.isbn13 == book.isbn13;
}
}
class Program
{
static void Main(string[] args)
{
Book book1 = new Book(9788998139018, "리버스 엔지니어링 바이블", "......");
Book book2 = new Book(9788998139018, "리버스 엔지니어링 바이블", "......");
Book book3 = new Book(9788992939409, "파이썬 3.6 프로그래밍", "......");
Console.WriteLine("book1 == book2 : " + book1.Equals(book2));
Console.WriteLine("book1 == book3 : " + book1.Equals(book3));
}
}
2) 오버로드(overload)
메서드 시그니처(method signature)는 메서드를 고유하게 규정할 수 있는 정보(이름, 반환 타입, 매개변수의 수, 개별 매개변수 타입)를 의미한다. 오버로드는 시그니처 중에서 반환 값은 무시하고 이름만 같은 메서드가 매개변수의 수, 개별 매개변수 타입만 다르게 재정의되는 경우를 말한다.
2.1) 메서드 오버로드(method overload)
C# 언어에서는 오버로드를 지원하기 때문에 다음과 같이 동일한 이름의 메서드로 일관성 있게 클래스를 작성할 수 있다.
ex)
class Mathematics
{
public int Abs(int value)
{
return (value >= 0) ? value : -value;
}
public double Abs(double value)
{
return (value >= 0) ? value : -value;
}
public decimal Abs(decimal value)
{
return (value >= 0) ? value : -value;
}
}
class Program
{
static void Main(string[] args)
{
Mathematics math = new Mathematics();
Console.WriteLine(math.Abs(-5));
Console.WriteLine(math.Abs(-10.052));
Console.WriteLine(math.Abs(20.01m));
}
}
2.2) 연산자 오버로드(operator overload)
연산자 역시 타입별로 재정의할 수 있다. static을 붙이는 이유는 객체의 존재 유무와 관계없이 연산이 되어야 하기 때문이다.
public static 타입 operator 연산자(타입1 변수명1, 타입2 변수명2)
{
//[타입]을 반환하는 코드
}
ex)
class Market
{
public int i_apple;
public int i_orange;
public override string ToString()
{
return "{i_apple : " + i_apple + ", i_orange : " + i_orange + "}";
}
public static Market operator + (Market Obj1, Market Obj2)
{
Market Obj3 = new Market();
Obj3.i_apple = Obj1.i_apple + Obj2.i_apple;
Obj3.i_orange = Obj1.i_orange + Obj2.i_orange;
//Console.WriteLine("===========================================");
//Console.WriteLine("Obj1 : " + Obj1);
//Console.WriteLine("Obj2 : " + Obj2);
//Console.WriteLine("===========================================");
//Console.WriteLine("operator + 호출됨");
return Obj3;
}
}
class Program
{
static void Main(string[] args)
{
Market market_1 = new Market();
Market market_2 = new Market();
market_1.i_apple = 10;
market_1.i_orange = 10;
market_2.i_apple = 100;
market_2.i_orange = 100;
//Market market_3 = new Market();
//market_3.i_apple = market_1.i_apple + market_2.i_apple;
//market_3.i_orange = market_1.i_orange + market_2.i_orange;
Console.WriteLine("가게1 : " + market_1);
Console.WriteLine("가게2 : " + market_2);
Market market_3 = market_1 + market_2;
Console.WriteLine("가게3 : " + market_3);
}
}
재정의된 메서드는 정적 유형이며 operator예약어와 함께 + 연산자 기호가 메서드 이름을 대신하고 있다.
ex) 복소수
class Complex // 복소수
{
int Real; // 실수
int Image; // 허수
public Complex(int Real, int Image)
{
this.Real = Real;
this.Image = Image;
}
public override string ToString()
{
if(Image >= 0)
{
return "{ " + Real + " + " + Image + "i }";
}
else
{
return "{ " + Real + " - " + (Image * -1) + "i }";
}
}
public static Complex operator + (Complex c1, Complex c2)
{
//c3 = new Complex(c1.Real + c2.Real, c1.Image + c2.Image);
return new Complex(c1.Real + c2.Real, c1.Image + c2.Image);
}
public static Complex operator - (Complex c1, Complex c2)
{
return new Complex(c1.Real - c2.Real, c1.Image - c2.Image);
}
public static Complex operator *(Complex c1, Complex c2)
{
return new Complex(c1.Real * c2.Real - c1.Image * c2.Image, c2.Real * c1.Image + c1.Real * c2.Image);
}
}
class Program
{
static void Main(string[] args)
{
Complex num1 = new Complex(3, 4);
Complex num2 = new Complex(5, 6);
Complex num3 = num1 + num2;
Complex num4 = num1 - num2;
Complex num5 = num1 * num2;
Console.WriteLine(num1);
Console.WriteLine(num2);
Console.WriteLine(num3);
Console.WriteLine(num4);
Console.WriteLine(num5);
}
}
연산자에 따른 오버로드 가능 여부
C# 연산자 | 오버로드 가능 여부 |
+, -, ~, ~, ++, --. ture, false | 단항 연산자는 모두 오버로드 가능(+,-는 부호 연산자) |
+, -, *, /, %, &, |, ^, <<, >> | 이항 연산자는 모두 오버로드 가능(+,-는 사칙 연산자) |
==, !=, <, >, <=, >= | 비교 연산자는 모두 오버로드할 수 있찌만 반드시 쌍으로 재정의해야 한다. == 연산자를 오버로드 했다면 != 연산자도 해야 한다. |
&&, || | 논리 연산자는 오버로드할 수 없다. |
[ ] | 배열 인덱스 연산자 자체인 대괄호는 오버로드할 수 없지만 C#에서는 이를 대체하는 별도의 인덱서 구문을 지원한다. |
(Type) x | 형변환 연산자 자체인 괄호는 오버로드할 수 없지만 대신 explicit, implicit를 이용한 대체 정의가 가능하다. |
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= | 복합 대입 연산자 자체는 오버로드할 수 없지만 대입이 아닌 +, -, *, / 등의 연산자를 오버로드하면 복합 대입 연산 구문이 지원된다. |
기타 연산자 | 오버로드할 수 없다. |
'스마트팩토리 > C#' 카테고리의 다른 글
13. 복습 (0) | 2020.06.25 |
---|---|
12. 클래스 간의 형변환, 중첩 클래스, 추상 클래스, 델리게이트 (0) | 2020.06.04 |
10. Object, Array, this, base, 다형성 (0) | 2020.06.02 |
9. 캡슐화, 정보 은닉, 프로퍼티, 상속, as, is (0) | 2020.06.01 |
8. 종료자, static, 네임스페이스 (0) | 2020.05.29 |