상속받은 필드(this.aaa)와 부모의 필드(base.aaa) 그리고 그냥 필드(aaa)
'끝나지 않는 프로그래밍 일기' 블로그의 강좌(https://blog.hexabrain.net/142) 에 나와있는 클래스 상속에 대한 내용을 공부중에 의문이 들어 예제 코드를 고쳐가면서 공부해 봄.
자식 클래스가 인스턴스화 되면서 생기는 필드의 정체에 대해 알아 봄.
using System;
namespace TTT
{
class Parent
{
public int num;
public Parent()
{
Console.WriteLine("부모클래스 생성자가 호출됨");
}
}
class Child : Parent
{
public Child(int aaa)
{
num = aaa;
this.num = aaa + 1;
Console.WriteLine("자식클래스 생성자가 호출됨. ");
Console.WriteLine("num은 {0} 이고, this.num은 {1} 입니다.", num, this.num);
//결과 this.num 이 결국 그냥 num 이다. 같은 놈이다.
Console.WriteLine("base.num은 {0} 입니다.",base.num);
//그런데 base.num도 같은 값임...
base.num++;//base.num에 1을 더해봤더니
Console.WriteLine("base.num++ 을 실행");
Console.WriteLine("base.num은 {0} 입니다.", base.num);
Console.WriteLine("그럼 this.num과 num은... {0}과 {1}이네요",this.num,num);
//그냥 this.num이나 num이나 base.num이나 같다.
}
public void DisplayValue()
{
Console.WriteLine("num의 값은 {0}입니다.", num);
}
}
class Child2 : Parent
{
public Child2(int bbb)
{
num = bbb;
Console.WriteLine("자식클래스2 생성자 호출됨.");
Console.WriteLine("num은 {0} 입니다.", num);
Console.WriteLine("base.num은 {0} 입니다.", base.num);
}
}
class Program
{
static void Main(string[] args)
{
Child cd = new Child(20);
cd.DisplayValue();
Child2 cd2 = new Child2(100);
}
}
}
결과는 아래와 같다.
이 예제에서 결국 자식(파생)클래스로 인스턴스를 만들었을 때, 그 자식 클래스 안에서는
aaa가 this.aaa와 동일한 것이고,
부모 클래스로부터 상속받은 필드가 자식클래스의 필드가 되버렸다.
즉, this.aaa 와 base.aaa 의 값은 동일하다.
this.aaa++을 하면 base.aaa도 ++가 되고, 반대로 base.aaa+++를 하면 this.aaa와 aaa도 ++가 된다.
이렇게 되버린 이유는, 자식 클래스에서 num의 선언을 새로이 해주지 않았기 때문이다.
자식클래스에서 새로이 같은 필드를 선언해서 따로 굴리려면
이 다음 예제에도 언급될 테지만, new 키워드를 써야 한다.
public new int num;
이렇게 새로이 선언해 주면 된다. 단, 생성자에서 선언하면 안되고 생성자 밖에 선언해야 한다. 즉 전체코드는 아래와 같이 된다.
using System;
namespace TTT
{
class Parent
{
public int num;
public Parent()
{
Console.WriteLine("부모클래스 생성자가 호출됨");
}
}
class Child : Parent
{
public new int num;
public Child(int aaa)
{
num = aaa;
this.num = aaa + 1;
Console.WriteLine("자식클래스 생성자가 호출됨. ");
Console.WriteLine("num은 {0} 이고, this.num은 {1} 입니다.", num, this.num);
//결과 this.num 이 결국 그냥 num 이다. 같은 놈이다.
Console.WriteLine("base.num은 {0} 입니다.", base.num);
//그런데 base.num도 같은 값임...
base.num++;//base.num에 1을 더해봤더니
Console.WriteLine("base.num++ 을 실행");
Console.WriteLine("base.num은 {0} 입니다.", base.num);
Console.WriteLine("그럼 this.num과 num은... {0}과 {1}이네요", this.num, num);
//그냥 this.num이나 num이나 base.num이나 같다.
}
public void DisplayValue()
{
Console.WriteLine("num의 값은 {0}입니다.", num);
}
}
class Child2 : Parent
{
public Child2(int bbb)
{
num = bbb;
Console.WriteLine("자식클래스2 생성자 호출됨.");
Console.WriteLine("num은 {0} 입니다.", num);
Console.WriteLine("base.num은 {0} 입니다.", base.num);
}
}
class Program
{
static void Main(string[] args)
{
Child cd = new Child(20);
cd.DisplayValue();
Child2 cd2 = new Child2(100);
}
}
}
결과는 아래와 같다.
부모클래스의 멤버(필드,메소드 등) 숨기기 (new 키워드)
위에서 살짝 다뤘지만 new 키워드를 이용해서 부모클래스에서 선언된 멤버 (필드, 메소드 등)를 자식클래스에서 다시 선언해서 쓸 수 있다. (이렇게 되면 메소드의 경우 오버라이드와 차이가 뭔지 모르겠다.)
using System;
namespace TTT
{
class Parent
{
public int x = 100;//생성자 밖에서 선언
public void A()
{
Console.WriteLine("부모클래스의 A() 메서드 호출");
}
}
class Child : Parent
{
public new int x = 200;//생성자 밖에서 선언
public new void A()
{
Console.WriteLine("자식클래스의 A() 메서드 호출");
Console.WriteLine("자식클래스의 x인 new x : {0}", x);
Console.WriteLine("부모클래스의 x인 base.x : {0}", base.x);
//드디어 부모클래스의 x의 값과 자식클래스의 x의 값이 달라졌다.
}
}
class Program
{
static void Main(string[] args)
{
Parent pr = new Parent();
pr.A();
Console.WriteLine("x : {0}\n\n", pr.x);
Child ch = new Child();
ch.A();
Console.WriteLine("x : {0}", ch.x);
}
}
}
결과는 아래와 같다.
다형성 및 캐스팅
[C# 때려잡기 - SeeRoE 블로그]
해당 블로그에서 다형성 및 캐스팅을 공부하려고 정리한 내용이 있어서 위의 링크에 퍼왔다.
캐스팅 및 타입변환
나름 정리가 잘 되어 있다.
다형성, 형변환 ★★★★★
역시 다형성과 형변환을 다룬 내용이다.
정말 정리가 잘되어 있고, '이것이 C#이다' 책보다 훨씬 더 이해가 잘 된다.
여기에 텍스트로 간단히 정리한 글도 있었는데 괜찮았다.
형변환 정리
특정타입의 참조변수 개념을 "몇 개의 화살표를 가지며, 모든 화살표는 가리키는 대상이 있어야 함" 으로 생각하면
조금 이해가 가는 것 같다.
Bamsunbic의 기술블로그[링크]의 내용을 기본으로 공부하면서 좀 더 이해가 갈 수 있게 정리해보았다.
다운캐스팅을 성공하려면 우선 Computer com = new NoteBook(); 처럼 인스턴스는 자식클래스로 만들고 부모클래스의 참조를 걸어줘야 한다. 그렇게 만든 부모클래스의 참조변수 com이 가리키는 실체는 멤버갯수가 많은 자식클래스의 객체인스턴스....(다스베이더 BGM);;
그렇게 해야 com을 자식형식으로 참조변환을 시켜도 (화살표 갯수가 많아져도) 실체는 멤버갯수가 많은 자식클래스 이므로 문제가 없다. NoteBook(자식클래스) note = com as NoteBook; 또는 NoteBook note = (NoteBook)com
여기서
(NoteBook)com
은 화살표3개짜리 부모Computer형식 참조변수 com의 참조를, 화살표5개짜리 자식NoteBook형식 참조로 변환시킨다는 것이다. com 자체를 변환시키는 것이 아니다. (이 코드 후에 비주얼 스튜디오에서 'com.'을 타이핑해보면 여전히 3개의 멤버만 뜨는 것을 확인할 수 있다.)
즉, com 이 가리키는 인스턴스를 새로이 가리키는 NoteBook형식의 새로운 참조
Bamsunbic의 기술블로그[링크] 를 쭉 내려가다 보면 'as 연산자와 is 연산자를 이용한 형변환' 부분에서
as 연산자 설명을 보면, 다운캐스팅을 하는 경우 프로그램 실행시 예외가 발생할 수도 있으므로, 오류를 발생하지 않고 형변환이 가능한지 확인할 때 사용한다고 되어 있다. 즉, 부모클래스 참조의 자식클래스 참조화 할 때 이용하는 것이다.
아래 코드에서 note=(NoteBook)com; 가 있는데 부모타입 참조변수인 com 을 (NoteBook)을 붙여서 자식타입으로 형변환을 시키고 이를 자식타입 참조변수 note에 저장시킨다.
Computer com = new NoteBook();
NoteBook note = null;
note = (NoteBook)com;//다운캐스팅. ①부모타입 참조변수를 ②자식타입으로 형변환시켜 ③자식타입 참조변수에 저장.
note.WebcamChatting();
이 코드가 문제가 없는 이유는 부모타입의 참조변수 com은 자식타입의 인스턴스 new NoteBook()를 가르키게 했기 때문이다.
이와 별도로 유튜브에서 다형성과 형변환에 대한 좋은 영상이 있어 가져온다. C#은 아니고 코틀린인가 하는 언어 강좌에 나온 내용이다.
'프로그래밍 > C# 일반' 카테고리의 다른 글
[노트] 메소드 숨기기(new)와 오버라이드(override) (0) | 2024.04.19 |
---|---|
[펌] C# 다형성:형변환 및 캐스팅 ★★☆☆☆ (0) | 2024.04.17 |
c# switch식과 튜플 패턴 매칭 (0) | 2024.04.14 |
[펌]C# 상속 클래스 부모의 생성자 호출 순서 (0) | 2024.04.09 |
Int32.Parse(), Convert.ToInt32(), Int32.TryParse() 차이 (0) | 2024.02.20 |