20. 클래스 심화

김민영·2023년 1월 31일
0

C# 기초 프로그래밍

목록 보기
14/18

🐳 멤버

: 멤버란 클래스의 구성 요소로, 아래의 종류가 있습니다.
→ 필드, 상수, 프로퍼티, 메소드, 이벤트, 연산자, 인덱서, 생성자, 종료자, 네스티드 타입

1. 필드

class User
{
	int _id;
    string _name;
    System.DateTime _birthday;  // 내장타입 아님
}
  • 클래스 범위에 선언된 변수로, 클래스(타입)를 구성하는 데이터입니다.
  • 필드의 타입은 반드시 내장타입일 필요는 없습니다.
  • 필드는 보통 private으로 선언하며, 이를 데이터 은닉이라고 합니다.

2. 상수

class User
{
	const int _id;
    const string _name;
    const System.DateTime _birthday;  // 내장타입이 아니기에 불가능
}
  • 프로그래머의 실수를 줄이고, 컴파일러 최적화에도 좋기 때문에 상수를 최대한 활용하면 좋습니다.
  • object를 제외한 내장타입만 상수로 선언할 수 있다는 한계가 있습니다.

▶ readonly 한정자

  • 클래스 타입 또한 수정할 수 없는 값으로 선언하길 원하는 경우 이용할 수 있습니다.
  • 필드를 정의할 때 사용할 수 있으며, readonly 필드는 생성자 안에서만 값을 할당할 수 있습니다.
  • 이외에도 다양한 곳에서 활용할 수 있습니다.
class A
{
	private readonly int _num;
    private readonly System.DateTime _createTime;
    
    public A
    {
    	_num = 10;  // 가능
        _createTime = DateTime.Now;  // 가능
    }
    
    public A(int n)
    {
    	_num = n;  // 가능
        _createTime = DateTime.Now;  //  가능
    }
    
    // 복사 생성자(Copy Constructor)의 예시
    public A(A other)
    	: this(other._num)
    {
    	
    }
    
    public void Foo()
    {
    	_num = 10;  // 생성자가 아닌 곳에서 할당하려 했으므로 오류 발생
    }
}

3. 메소드

class Warrior
{
	private int _hp = 100;
    private int _atk = 10;
    
    public void TakeDamage(int damage)
    {
    	_hp = Math.Max(0, _hp - damage);
        
        if(_hp == 0)
        {
        	Die();
        }
    }
    
    public void Die() => System.Console.WriteLine("으아악");
}
  • 클래스 내부에 정의된 함수로, 클래스가 할 수 있는 행위입니다.
  • 보통 필드를 직접 수정하지 않고, 메소드를 호출해 데이터를 조작합니다.
    → 의도치 않은 데이터 수정을 방지할 수 있습니다.

▶ 메소드의 오버로딩 (Overloading)

1) 오버로딩이란?

  • 같은 이름의 메소드를 여러개 정의하는 것
  • 입력되는 데이터가 여러 경우일 때, 헬퍼 함수를 정의할 때 사용할 수 있습니다.
  • 매개변수를 다르게하여 정의할 수 있습니다.

2) 시그니처 (Signature)

  • 함수의 입력과 출력을 정의하는 것
  • 접근 수준, 한정자, 반환값, 함수의 이름, 매개변수로 구성
  • 오버로딩을 위해서는 필수적으로 매개변수가 달라야하며, 그 외 다른 요소들만 다른 경우에는 오류가 발생합니다.
public void Foo(int a) { }

public void Foo(double a) { }  // 가능, 매개변수의 타입이 달라짐

public void Foo(int b) { }  // 불가능, 결국 int 타입의 매개변수 하나로 같은 형식

public int Foo (int a) { }  // 불가능, 반환값만 바꿔선 안됨

private void Foo (int a) { }  // 불가능, 접근 수준만 바꿔선 안됨

3) 헬퍼 함수

class Regex
{
	// 헬퍼 함수
	public static int Count(string input, string pattern)
    {
    	Count(input, pattern, RegexOption.IgnoreCase);
    }
    
    // 함수의 원형
    public static int Count(string input, string pattern, RegexOptions options)
    {
    	...
    }
}
  • 매개변수가 더 적은 쪽이 헬퍼함수입니다.
  • 더 적은 매개변수를 입력해도 원형 함수의 기능을 이용할 수 있도록 하여, 프로그래머가 더욱 편리하게 코드를 작성할 수 있도록 합니다.

▶ 확장 메소드

1) 이미 존재하는 타입(클래스)에 추가한 프로그래머의 메소드를 의미합니다.

  • static 클래스에는 추가할 수 없습니다.

2) 확장 메소드는 정적 메소드(static)이어야 합니다.

3) 첫번째 매개변수가 this 포인터 확장시킬 타입(클래스) 매개변수명 여야합니다.

4) Regex 클래스에 Count( ) 메소드를 추가해 사용하는 예제

static class RegexExtentions
{
	public static int Count(this Regex regex, string str)
    {
    	MatchCollection matches = regex.Matches(str);
        return matches.Count;
    }
}

class Program
{
	public static void Main()
    {
    	Regex regex = new Regex(@"(pAPp)");
        string input = "ApPApPpAPpApPAp";
        regex.Count(input);  // 확장 메소드 Count를 호출해 사용
    }
}

4. 프로퍼티

▶ Getter와 Setter

class Character
{
	private int _hp;
    
	public void SetHp(int newHp) => _hp = newHp;  // Setter
	public in GetHp() => _hp;  // Getter
}

1) Getter
: 데이터를 수정할 수 있는 메소드

2) Setter
: 데이터에 접근할 수 있는 메소드

▶ get; set; (프로퍼티, 메소드)

1) c#에서 Getter와 Setter를 더욱 편리하게 사용하기 위해 제공하는 기능
→ 즉, 프로퍼티 또한 메소드입니다.

2) 일부 프로퍼티 접근자의 접근 수준을 다르게 할 수 있습니다.

class Character
{
	public int HP { get; set; }
	public int Mp { get; private set; }
}

5. 생성자

▶ 생성자란

1) 클래스의 객체를 생성할 때, 객체의 데이터를 초기화하는 메소드 입니다.
2) 생성자는 new 연산자와 함께 호출됩니다.
3) 생성자는 반환값이 없으며, 클래스의 이름과 같습니다.
4) 생성자를 정의하지 않을 경우, 컴파일러가 매개변수가 없는 생성자를 자동으로 생성합니다.
5) 별도로 지정하지 않는 경우, 모든 데이터는 기본값으로 초기화되며 초기값이 있는 경우 그 값을 사용합니다.
6) 매개변수가 있는 생성자를 하나라도 정의한 경우, 매개변수가 없는 생성자는 자동생성되지 않습니다.

▶ 생성자의 오버로딩

1) 생성자 또한 메소드의 일종으로, 오버로딩이 가능합니다.
2) 생성자 오버로딩 시 다른 생성자를 호출할 수 있으며 이 때 this라는 키워드를 사용합니다.
3) this 키워드 사용시 콜론(:) 사용에 유의합니다.

Class Coord
{
	public Coord()
		: this(0, 0)  // 1. 매개변수가 int 타입 두개인 생성자 호출
	{
		// 3. int 타입 두개인 생성자 부분 실행 후 여기로 넘어와서 이 블록 내용까지 실행
	}

	public Coord(int x, int y)  // 2. 실행흐름이 여기로 이동
	{
		X = x;
		Y = y;
	}
}

public void Main()
{
	Coord coord = new Coord();  // 매개변수가 없는 생성자 호출할 경우 실행흐름
}

🐳 this 포인터

인스턴스들의 데이터는 서로 영향을 주고받지 않고, 독립적입니다.
독립적인 데이터를 사용할 수 있는 핵심적인 이유가 바로 this 포인터 입니다.

class Character
{
	private int _hp = 100;
    
    public void TakeDamage(int damage) => _hp -= damage;
}

Character character1 = new Character();
Character character2 = new Character();

character1.TakeDamage(10);

→ 위와 같은 코드를 실행했을 때, 사실 TakeDamage는 명시적으로 작성된 인수인 10과 함께 character1의 주소 또한 인수로 전달합니다.
→ _hp -= damage;는 사실 this._hp -= damage; 와 같으며 this는 (character1의 주소를 전달받아) 현재 메소드를 호출한 인스턴스를 참조하기 때문에, 인스턴스의 데이터를 구분하여 동작할 수 있는 것입니다.

→ this 포인터는 인스턴스 메소드에서만 유효합니다.

🐳 정적 멤버

1. static

1) static 한정자를 사용해 멤버를 정적으로 만들 수 있습니다.

  • 필드, 메소드, 프로퍼티, operator, event, constructor에 가능

2) 정적 멤버란, 인스턴스끼리 공유하는 멤버를 의미합니다.

class Character
{
	private static int s_hp = 100;
	public void TakeDamage(int damage) => s_hp -= damage;
}

Character character1 = new Character();
Character character2 = new Character();

character1.TakeDamage(10);
character2.TakeDamage(10);
Console.WriteLine(Character.s_hp);  // 80이 출력된다.
  • 정적 멤버는 클래스 레벨에서 정의되는 것으로, 클래스 이름을 통해서 접근
    → character1.s_hp가 아닌 Character.s_hp

2. 정적 필드

1) 정적 필드는 런타임에 초기화 됨
2) 정적 필드를 초기화하기 위해 정적 생성자를 사용할 수 있습니다.
3) 정적 필드는 접근 한정자 및 매개변수를 가지지 않습니다.
4) 오직 한가지만 존재하며, 직접 호출할 수 없습니다.
5) 정적 필드는 스택 혹은 힙 영역을 사용하지 않으며, 데이터 영역을 사용합니다.
6) 정적 멤버만 모아놓기 위해 정적 클래스를 만들 수 있으며, 이 경우 인스턴스를 만드는 것이 불가능해집니다.



참고자료

0개의 댓글