객체지향 프로그래밍 언어(Object - Oriented Programming Language, OOP)
C# 말고 다른 언어를 배워 본 적이 없어서 객체지향 프로그래밍 언어와 그렇지 않은 언어들과의 차이점, 그로 인한 장단점은 잘 모른다. 배운 걸로 정리해보면 객체지향 언어는 변수, 속성, 메소드 등을 모두가 다같이 공유하는 코드 평야에 놓아두고 골라쓰는 것이 아니라 어떤 객체를 만들어 놓고 그 객체가 각자의 변수, 속성, 메소드 등을 갖고 있어 다른 객체들이 서로의 것에 접근하는 것에 제한을 두는 언어다. 어떻게 보면 객체를 만들 때마다 매번 필요한 요소들을 만들어줘야하니 "메모리를 많이 먹지 않나? 비효율적인 것 아닌가?" 라는 생각을 할 수도 있지만(나도 처음엔 그렇게 느꼈다.) 배우면 배울수록 만약 이 기능이 없었다면 디버깅에 굉장히 애먹었을 것 같다는 생각을 하게 됐다. 언어 자체에서 잘못된 접근에 강력한 제한을 걸어두고 있기 때문에 작성자가 할 수 있는 실수를 미리 막을 수 있고 사실 메모리 문제는 지금 하드웨어로는 전혀 문제가 될일이 아니기 때문에 나로서는 OOP를 첫 언어로 배운 것에 다행이라고 느껴지기도 한다.
클래스(Class)
객체를 만들기 위한 청사진이며 그런 정보를 담는 복합 자료형이다. (거의) 모든 객체는 클래스로부터 만들어진다. 때문에 클래스를 잘 다룬다면 OOP를 아주 강력하게 활용할 수 있다. OOP의 시작과 끝이다. 클래스는 생성자, 필드, 속성, 메소드, 이벤트 등으로 구성되며 필요한 것만 담아서 만들면 된다.
using System;
class NewClass
{
// 새로운 클래스를 만들었다.
// 여기에 이 클래스로 객체를 만들어낼 "생성자"를 정의하거나
// 이 클래스로부터 만들어진 객체들이 가진 데이터들을 "필드"로 지정하고
// 필드의 값을 "속성"으로 다룰 수 있다.
// 그리고 객체들이 가진 기능인 "메소드"를 정의할 수 있다.
// "이벤트"는 아직 뭔지 정확히 잘 모른다.
}
생성자(Constructor)
청사진으로 객체를 만들도록 한다. 클래스 명과 똑같은 메소드의 형태를 갖고 있고 출력형식 없이 정의해주면 된다. 본질적으로 메소드이기 때문에 생성만 해줄수도 있고 생성과 동시에 여러 작업을 처리하게 하는 것도 가능하다. 생성자가 있는 클래스는 new 키워드를 통해 객체로 인스턴스화 할 수 있다.
using System;
public class NewClass
{
private int a;
public NewClass()
{
a = 10;
// 생성과 동시에 이 클래스의 객체가 갖는 필드를 지정해준다.
}
}
class Program
{
static void Main()
{
NewClass A = new NewClass();
// new 키워드로 객체 인스턴스화
}
}
접근제한자(Access Modifier)
다른 클래스, 네임스페이스의 코드가 어떤 필드에 마음대로 접근할 수 있다면 필드의 이름을 잘못 지정해서 겹치거나 착각할 경우 엉뚱한 값이 엉뚱한 필드에 들어갈 수 있다. 이런 사고를 막기위해 만들어진 객체지향 언어이기 때문에 기본적으로 서로 다른 클래스는 private 접근 제한자로 수식되어있다. 이 클래스 안의 데이터는 이 클래스 안에서만 접근할 수 있다는 의미다. 그래서 클래스에 접근 제한자를 붙여주지 않으면 private 상태이기 때문에 다른 클래스에서 접근이 불가능하다. 인스턴스화도 당연히 안된다. 다른 클래스에 접근하려면 클래스가 public 수식을 받아야한다. 그리고 클래스 내의 멤버들도 public 수식을 받아야 접근할 수 있다. 클래스 내에서도 조심해야할 특정 필드, 메소드 들은 private으로 수식해 접근을 막을 수도 있다. 생성자는 보통 다른 클래스에서 이 클래스로 객체를 만들어 인스턴스화하기 위해 사용하기 때문에 public으로 수식해준다. 위의 코드에서 NewClass 클래스와 그 생성자 NewClass( )는 모두 public 키워드로 수식을 받고있다. 하지만 필드에 있는 정수 a는 private 수식을 받기 때문에 Program 클래스에서는 이 변수에 접근할 수 없다. 하지만 생성자에서 이미 이 변수에 값을 지정해줬다. 이걸 볼 수 없을 뿐이다.
필드와 속성(Field & Property)
private 필드의 내용은 밖에서 확인하거나 수정할 수 없다. 그럼 이런 필드는 생성해서 어디다 쓸까? 필드 안에 있는 메소드에 필요한 값으로 쓸 수 있다. 하지만 밖에서도 써야하는 필드는? get, set 키워드를 활용하면 필드를 속성으로 꺼내오고 고칠 수 있다. get은 읽기 전용 키워드로 필드의 값을 가져와 밖으로 보내줄 수 있게 한다. set 키워드는 value 키워드와 함께 밖에서 가져온 데이터를 필드로 전달해줄 수 있다. 필드와 속성의 차이가 뭔지 공부하는 내내 흐릿했다. 필드는 클래스 내에 비공개로 유지되는 데이터들이다. 속성은 이 필드를 외부 클래스에 노출시킨다.
using System;
public class NewClass
{
private int a; // 이 필드는 밖에서 접근할 수 없다.
public NewClass()
{
a = 10;
}
public int A
{
get { return a; } // 필드의 값을 밖으로 노출시킨다.
set { a = value; } // 밖에서 가져온 value 값을 a로 보내준다.
}
}
class Program
{
static void Main()
{
NewClass NC = new NewClass();
int a = NC.A; // 여기 a는 이름만 같을 뿐 다른 공간에 할당되어 있다.
Console.WriteLine($"a = {a}, NC.A = {NC.A}");
NC.A = 20; // 속성을 통해 NewClass 의 a에 접근한다.
Console.WriteLine($"a = {a}, NC.A = {NC.A}");
}
}
// 결과
// a = 10, NC.A = 10
// a = 10, NC.A = 20
메소드(Method)
정해진 코드에 따라 일을 처리하고 반환하는 역할을 한다. 입력과 출력이 각각 있거나 없기도 하고 있는 경우 그 형식을 자유롭게 지정할 수 있다. 여러개의 입력을 받기도 하며 return 키워드 말고도 out 키워드로 출력을 여러개로 보낼 수도 있다. 가장 기본적이고 많이 사용하는 메소드는 Main( )이다. 이 메소드는 프로그램의 진입점을 나타낸다. 메소드를 잘 활용하면 반복되는 복잡한 작업을 한 번 정의해놓고 단 한 줄의 호출로 간략화할 수 있다.