한정자
| 접근 한정자 | 내용 |
|---|---|
| public | 클래스의 내부/외부에서 모두 접근 가능 |
| protected | 클래스의 외부에서 접근 불가/파생 클래스에서 접근 가능 |
| private | 클래스의 내부에서만 접근 가능 |
| internal | 같은 어셈블리에 있는 코드에서 public으로 접근 가능/다른 어셈블리에 있는 코드에서는 private의 접근 수준 |
| protected internal | 같은 어셈블리에서 protected로 접근 가능/다른 어셈블리에 있는 코드에서는 private의 접근 수준 |
| private protected | 같은 어셈블리에 있는 클래스에서 상속받은 클래스 내부에서만 접근 가능 |
여기서 어셈블리는 하나 이상의 원본 파일을 컴파일하여 생성된 DLL(실행파일 또는 동적 라이브러리)을 말한다.
주의할 점은 C#에서 접근 한정자를 수식하지 않을 경우 무조건 private로 자동 지정된다는 점이다.
상속
C#에서는 자식 클래스(파생 클래스)와 부모 클래스(기반 클래스)가 있다. 자식 클래스는 부모 클래스를 상속 받아 부모 클래스의 멤버와 메소드를 가질 수 있다.
자식 클래스 뒤에 콜론을 붙이고 부모 클래스를 붙여줌으로써 상속이 이루어 진다.
1
2
3
4
5
6
7
8
9
class Base {
public BaseMethod() {
...
}
}
class Derived : Base {
...
}
자식 클래스는 객체를 생성할 때 부모 클래스 생성자를 호출하고 자신의 생성자를 호출한다. 소멸될 때는 반대로 자신의 소멸자를 호출하고 부모 클래스의 소멸자를 호출한다.
자식 클래스의 객체를 생성할 때 부모 클래스의 생성자에 매개변수를 전달할 때는 base 키워드를 사용한다. base 키워드를 통해서 부모 클래스의 멤버에 접근할 수 있다.
메소드 숨기기
메소드 숨기기란 부모 클래스에서 구현된 메소드를 숨기고 자식 클래스에 새로 정의된 메소드만 노출시키는 것이다.
자식 클래스의 메소드에 new 키워드를 붙여서 새로 정의한다.
이 경우 부모 클래스 객체를 생성할 경우 숨겨진 부모 클래스 버전의 메소드를 불러올 수 있다.
1
2
3
4
5
6
7
8
9
10
11
class Base {
public int Add(int num1, int num2){
return num1 + num2;
}
}
class Derived : Base {
public new int Add(int num1, int num2){
return num1 + num2 + 100
}
}
오버라이딩
부모는 자식이 여럿일 수 있다. 오버라이딩은 그런 여러 자식들을 위한 것이다. 오버라이딩은 부모 클래스의 메소드를 자식들이 각자의 개성을 살려 재정의할 수 있게 한다.
1
2
3
4
5
6
7
8
9
10
11
class Base {
public virtual void Character() {
...
}
}
class Derived : Base {
public override void Character() {
...
}
}
메소드를 오버라이딩하기 위한 조건은 virtual 키워드로 한정되야 한다는 점이다.
virtual 키워드로 한정된 부모 클래스의 메소드는 무한히 오버라이딩 될 수 있다. 이러한 문제점을 방지하기 위해서 sealed 키워드를 사용해서 다시 오버라이딩 되는 것을 방지할 수 있다.
단 sealed 한정자는 virtual 키워드로 한정된 메소드를 override로 재정의한 메소드에만 붙일 수 있다.
1
2
3
4
5
6
7
8
9
10
11
class Base {
public virtual void Character() {
...
}
}
class Derived : Base {
public sealed override void Character() { // 이제 더이상 override 할 수 없음
...
}
}
메소드 숨기기는 호출 시 객체의 실제 타입에 따라 어느 메소드가 호출되는지 가 결정된다. 오버라이딩은 객체의 타입과 관계없이 자식 클래스의 오버라이딩된 메소드가 호출된다.
메소드 숨기기는 컴파일 시점에 메소드 호출이 결정되는 정적 바인딩에 의해 동작한다. 즉 메소드 호출 시점에 변수가 어떤 타입으로 선언되었는지를 기준으로 메소드를 결정하게되며, 이로 인해 객체를 생성할 때 사용한 클래스 타입의 메소드를 호출하게 된다.
메소드 오버라이딩은 런타임 시점에서 메소드 호출이 결정되는 동적 바인딩에 의해 동작한다. 즉 인스턴스 타입(생성된)에 따라 호출할 메소드를 결정한다.
컴파일 시점은 코드를 컴파일러가 기계어로 번역되는 순간이니 선언된 타입(참조 타입)을 바라보는 것이니 정적 바인딩으로 이핼할 수 있다.
런타임 시점은 프로그램이 실제로 샐행되는 순가이니 이미 객체가 메모리에 할되어 있으며 객체의 타입(인스턴스 타입)을 바라보고 이것은 동적 바인딩이라 할 수 있다.