클래스를 이야기하려면 먼저 객체지햐 프로그래밍(Object Oriented Programming) 을 알아야한다.
객체지향 프로그래밍은 OOP 라고도 하는데, 코드 내 모든 것을 객체(Object) 로 표현하려는 프로그래밍 패러다임을 뜻한다.
여기서 객체는 세상의 모든 것을 지칭하는 단어로 사람, 연필, 자동차 등 모든 것을 객체로 볼 수 있다. 객체의 주요 특징으로 두 가지를 뽑아볼 수 있다. 바로 속성과 기능이다.
사람이라는 객체의 속성으로는 피부색, 키, 몸무게 등을 뽑을 수 있고, 기능으로는 걷기, 뛰기, 보기, 듣기 등을 뽑을 수 있다.
이러한 객체의 속성과 기능을 C# 코드를 통해 표현하자면 속성은 데이터로, 기능은 메소드로 표현할 수 있다. 정리하자면 객체는 데이터와 메소드로 이루어진다.
클래스는 객체를 만들기 위한 청사진이다. 클래스가 자동차 설계도라면, 객체는 생산된 실제 자동차라고 할 수 있다.
클래스는 객체가 가지게 될 속성과 기능을 정의하지만 실체를 가지지는 않는다. 클래스를 이용해 만든 객체가 실체를 가진다. 동일 클래스로 객체 3개를 만들면, 이 객체는 서로가 구분되는 고유한 실체를 가지며 저마다 메모리 공간을 차지한다.
string a = "123";
string b = "Hello";
위 코드에서 string 은 C#에서 이미 정의된 문자열을 다루는 클래스이고, a 와 b 는 객체이다.
다시 말해, string 은 문자열을 담는 객체를 위한 청사진이고, a 와 b 는 실제로 데이터를 담을 수 있는
실제 객체이다. a 와 b 를 일컬어 string 의 실체(Instance) 라고 한다.
클래스는 class 키워드를 이용해서 선언한다.
class 클래스_이름
{
// 데이터와 메소드
}
클래스 안에 선언된 변수들을 일컬어 필드(Field) 라고 하며, 필드와 메소드를 비롯하여 프로퍼티, 이벤트 등 클래스 내에 선언된 요소들을 일컬어 멤버(Member) 라고 한다.
클래스는 청사진일뿐, 실체(인스턴스)가 아니다. 따라서 인스턴스를 만들어야 한다.
new 키워드와 클래스의 생성자를 이용해 클래스의 객체를 생성할 수 있다.
클래스_이름 객체이름 = new 클래스_생성자;
using System;
namespace Class
{
class Cat
{
public string Name;
public string Color;
public void Meow()
{
Console.WriteLine($"{Name} : Meow");
}
}
class MainApp
{
static void Main(string[] args)
{
Cat kitty = new Cat();
kitty.Color = "White";
kitty.Name = "Kitty";
kitty.Meow();
Console.WriteLine($"{kitty.Name} : {kitty.Color}");
Cat nero = new Cat();
nero.Color = "Black";
nero.Name = "Nero";
nero.Meow();
Console.WriteLine($"{nero.Name} : {nero.Color}");
}
}
}
Kitty : Meow
Kitty : White
Nero : Meow
Nero : Black
객체가 생성될 때는 생성자(Constructor) 가 호출된다.
생성자는 클래스의 이름과 같고 반환 형식이 없다.
객체를 생성하는 시점에 객체의 필드를 원하는 값으로 초기화 할때 생성자를 이용할 수 있다.
class 클래스_이름
{
한정자 클래스_이름(매개변수_목록) // 생성자
{
//
}
// 필드
// 메소드
}
using System;
namespace Constructor
{
class Cat
{
public Cat(string name, string color)
{
Name = name;
Color = color;
}
public void Meow()
{
Console.WriteLine($"{Name} : Meow");
}
public string Name;
public string Color;
}
class MainApp
{
static void Main(string[] args)
{
Cat kitty = new Cat("Kitty", "White");
kitty.Meow();
Console.WriteLine($"{kitty.Name} : {kitty.Color}");
}
}
}
Kitty : Meow
Kitty : White
클래스를 선언할 때 명시적으로 구현하지 않아도 컴파일러는 생성자를 만들어 준다. 이렇게 컴파일러가 자동으로 만들어주는 생성자를 기본 생성자라고 한다.
만약 프로그래머가 생성자를 하나라도 직접 정의하면 C# 컴파일러는 기본 생성자를 만들지 않는다.
using System;
namespace Constructor
{
class Cat
{
public void Meow()
{
Console.WriteLine("Meow");
}
}
class MainApp
{
static void Main(string[] args)
{
Cat kitty = new Cat(); // 기본 생성자 호출
kitty.Meow();
}
}
}
Meow
생성자도 메소드와 마찬가지로 오버로딩이 가능하다. 따라서 다양한 종류의 생성자를 준비해놓을 수 있다.
using System;
namespace Constructor
{
class Cat
{
public Cat()
{
Name = "";
Color = "";
}
public Cat(string name, string color)
{
Name = name;
Color = color;
}
public string Name;
public string Color;
}
class MainApp
{
static void Main(string[] args)
{
Cat kitty = new Cat();
kitty.Name = "Kitty";
kitty.Color = "White";
Console.WriteLine($"{kitty.Name} : {kitty.Color}");
Cat nero = new Cat("Nero", "Black");
Console.WriteLine($"{nero.Name} : {nero.Color}");
}
}
}
Kitty : White
Nero : Black
객체가 소멸될 때는 종료자(Finalizer) 가 호출된다.
종료자는 클래스 이름 앞에 ~ 를 붙인 꼴이고, 매개변수 및 반환값이 없고, 한정자도 사용하지 않는다.
또한, 오버로딩도 불가능하며, 직접 호출할 수도 없다.
종료자는 CLR 의 가비지 컬렉터가 객체가 소멸되는 시점을 판단해서 종료자를 호출해 준다.
class 클래스_이름
{
~클래스_이름()
{
//
}
// 필드
// 메소드
}
종료자를 소개하기는 했지만, 다음과 같은 이유로 가급적 종료자를 사용하지 않는 것이 좋다.
CLR 의 가비지 컬렉터가 언제 동작할지 예측할 수 없다.
CLR 의 가비지 컬렉터는 동작할 시점을 알 수 없다. 객체의 사용이 끝난 직후가 될 수도 있고, 10분 후가 될수도 있다. 중요한 자원을 종료자에서 해제하도록 놔뒀다가 얼마 가지 않아 자원이 금세 부족해지는 현상을 겪을지도 모른다.
프로그램의 성능이 저하 될 수 있다.
종료자를 명시적으로 구현하면 가비지 컬렉터는 클래스의 족보를 타고 올라가 객체로부터 상속받은
Finalize() 메소드를 호출한다. 이렇게 되면 프로그램의 성능 저하를 초래할 확률이 높다.
또한, CLR 의 가비지 컬렉터는 우리보다 훨씬 더 똑똑하게 객체의 소멸을 처리할 수 있다. 따라서, 생성은 생성자에, 뒤처리는 가비지 컬렉터에 맡기는 편이 좋다.
using System;
namespace Finalizer
{
class Cat
{
public Cat()
{
Name = "";
Color = "";
}
public Cat(string name, string color)
{
Name = name;
Color = color;
}
~Cat()
{
Console.WriteLine($"{Name} : 소멸");
}
public string Name;
public string Color;
}
class MainApp
{
static void Main(string[] args)
{
Cat kitty = new Cat("Kitty", "White");
Console.WriteLine($"{kitty.Name} : {kitty.Color}");
Cat nero = new Cat("Nero", "Black");
Console.WriteLine($"{nero.Name} : {nero.Color}");
}
}
}
Kitty : White
Nero : Black
Kitty : 소멸
Nero : 소멸
위 프로그램의 출력 결과 중 마지막 두 줄은 실행할 때마다 달라질 수 있다. 객체의 소멸을 담당하는 가비지 컬렉터가 언제 동작할지는 예상할 수 없기 때문이다.