개체 만들기
우리는 앞서 프레임워크에서 제공하는 수많은 클래스들을 사용해보았습니다. 이번 시간에는 이러한 클래스와 개체들을 직접 만들어 보겠습니다. 개체(Object, 오브젝트, 객체)는 설계도인 클래스로부터 만들어진 하나의 인스턴스(실체, 구성 요소)를 말합니다.
이번 강의를 한 줄로 정의하면 다음과 같습니다.
> // 개체(Object, 오브젝트): 설계도인 클래스로부터 만들어진 하나의 인스턴스(실체, 구성 요소)
NOTE
박용준 강사는 Object
를 번역할 때 객체
용어를 사용하지 않고 개체
로 표현합니다.
클래스와 개체
class
키워드로 생성한 것을 클래스라고 하고, 이러한 클래스를 new
키워드를 사용해 새로운 이름으로 만든 것을 개체(object)라고 합니다.
다음 코드 조각은 클래스를 만들고 클래스의 인스턴스를 생성하는 내용입니다.
코드: ClassAndObject.cs
// ClassAndObject.cs
> //[1] ClassCode 이름의 클래스(Class) 만들기
> public class ClassCode
. {
. // Empty
. }
>
> //[2] 특정 클래스로부터 objectCode1, objectCode2 이름의 개체(Object) 만들기
> ClassCode objectCode1 = new ClassCode();
> objectCode1.GetHashCode()
1219419
> ClassCode objectCode2 = new ClassCode();
> var objectCode2 = new ClassCode();
> objectCode2.GetHashCode()
9874484
클래스는 개체를 만들기 위한 설계도로 기억하세요. 설계도는 하나이지만, 그 설계도로부터 만들어진 개체는 여러 개일 수 있습니다.
코드에서처럼 objectCode1
과 objectCode2
변수는 개체입니다. 모든 개체는 GetHashCode()
메서드를 호출하여 고유의 키값을 제공받을 수 있습니다. 사용하는 클래스는 ClassCode
로 동일하지만 이 클래스를 통해 만들어진 개체들은 서로 다른 인스턴스(Instance)입니다.
GetHashCode()
메서드의 Java 기능은 hashCode()
메서드입니다.
개체와 인스턴스
앞서 많은 수의 닷넷 API를 다뤄보면서 new
키워드로 클래스로부터 새로운 개체
(클래스의 인스턴스
)를 생성하고 사용하였습니다.
클래스로부터 개체를 생성하는 것을 클래스의 인스턴스 생성이라고 합니다. 클래스의 인스턴스는 다음 코드 조각과 같은 모양으로 지금껏 사용해 왔습니다.
클래스이름 개체이름 = new 클래스이름(); // 클래스이름 클래스의 인스턴스 생성
Car car = new Car(); // Car 클래스의 인스턴스 생성
Product p = new Product(); // Product 클래스의 인스턴스 생성
인스턴스는 클래스(설계도)로부터 만들어진 새로운 실체(Instance)입니다. 개체, 객체, 오브젝트, 인스턴스는 책과 강의에서는 동일한 단어로 표현합니다. 실생활에서의 예를 들면, 자동차 설계도로부터 만들어진 하나의 자동차를 자동차 개체 또는 자동차 인스턴스라 표현합니다. C#은 모든 개체가 특정 형식을 지니는 강력한 형식(Strongly Typed)의 언어라고 표현합니다. 하나의 클래스에서 여러 개의 개체를 만들 수 가 있습니다. 클래스는 그 자체가 하나의 설계도(템플릿)이기에 그 설계도로부터 여러 개의 개체(인스턴스)를 생성해낼 수 있습니다. 개체 생성은 메모리가 허락하는 동안 계속해서 만들어낼 수 있습니다.
TIP
변수를 선언할 때 명확한 타입을 지정하는 언어를 강력한 형식의 언어라고 합니다. 영어로는 Strongly Typed로 표현하고 줄여서 강타입 언어라고도 합니다. 예를들어, C#과 Java가 강타입 언어이고 파이썬과 자바스크립트가 약타입 언어입니다.
값형과 참조형
아직은 구분하기 어려울 수 있는 내용인데요. 잠시 읽어보고 넘어가세요.
닷넷에서 데이터 형식을 다룰 때 종종 값형(Value Type)과 참조형(Reference Type)을 말하는데, 클래스는 참조형입니다. new
키워드 뒤에 오는 클래스명()
는 뒤에서 다룰 생성자(Constructor)입니다. new
키워드를 사용하여 클래스에 기본으로 생략된 채 만들어지는 생성자를 실행하여 개체를 생성하는 역할을 합니다. 새롭게 class
키워드로 생성한 클래스는 기본 제공 형식이 아닌 사용자 정의 데이터 형식으로 참조형 개체라고 합니다. 클래스의 인스턴스는 인스턴스 변수라고 지금까지 사용해 내용 그대로입니다.
클래스의 인스턴스인 개체 만들기
이번에는 개체를 만드는 방법을 알아보겠습니다. 코드에는 추가로 namespace
키워드를 사용하여 클래스 2개를 묶어서 관리했습니다.
코드: ObjectNote.cs
// ObjectNote.cs
using System;
namespace ObjectNote
{
//[1] 클래스 생성
public class Counter
{
//[2] 인스턴스 멤버 생성
public void GetTodayVisitCount()
{
Console.WriteLine("오늘 1234명이 접속했습니다.");
}
}
class ObjectNote
{
static void Main()
{
//[A] 클래스의 인스턴스 생성
Counter counter = new Counter();
//[B] 개체(인스턴스)이름.멤버이름으로 클래스의 멤버 호출
counter.GetTodayVisitCount();
}
}
}
오늘 1234명이 접속했습니다.
[1]
번 코드처럼 클래스를 생성할 때에는 따로 정적 멤버와 인스턴스 멤버에 대한 내용을 지정하지 않습니다. 클래스 안에는 정적과 인스턴스 멤버를 원하는 만큼 만들 수 있습니다.
[2]
번 코드에서는 static
이 빠진 형태로 메서드를 만들어 인스턴스 멤버인 인스턴스 메서드를 만들었습니다.
특정 클래스의 인스턴스 멤버를 호출하려면 [A]
코드 영역처럼 클래스의 인스턴스를 생성합니다. 그런 후 [B]
코드 영역처럼 인스턴스.멤버 형태로 호출해서 사용합니다.
내용 없는 클래스
클래스에서는 내용 없는 클래스도 존재합니다. 물론 현실적으로 사용되지 않습니다. 다음 내용을 C# 인터랙티브에 입력한 뒤 실행해보세요. 프로젝트 기반 소스는 ClassPass.cs 파일에 있습니다.
(1) 먼저 멤버가 하나도 없는 Car
클래스를 만듭니다.
> class Car
. {
. // Empty
. }
(2) Car
클래스의 인스턴스인 car1
을 만들고 GetHashCode()
메서드를 호출하면 고유한 키 값이 출력됩니다. GetHashCode()
는 컴퓨터와 실행 시점에 따라 다르게 표시됩니다.
> Car car1 = new Car();
> car1.GetHashCode()
42194754
(3) 새로운 개체를 만들고 GetHashCode()
메서드를 호출하면 호출할 때마다 서로 다른 고유 키 값일 출력되는 것을 알 수 있습니다. 당연한 얘기겠지만, 클래스 자체도 하나의 데이터 형식이기에 Car
클래스 대신에 var
키워드로 클래스 형식의 변수(개체)를 생성할 수 있습니다.
> var car2 = new Car();
> car2.GetHashCode()
66166301
인스턴스 메서드
클래스내에 선언된 메서드 중에서 static
키워드가 붙지 않은 메서드를 인스턴스 메서드라고 합니다. 인스턴스 메서드를 호출하려면 클래스의 인스턴스를 생성하여 개체를 만들어야 합니다. 클래스의 인스턴스를 생성하여 개체를 만들려면 new
키워드를 사용합니다.
예를 들어, Car
클래스의 인스턴스를 생성하는 모양은 다음과 같습니다.
Car car = new Car();
일반적으로 클래스 이름은 대문자로 시작하고 개체 이름은 소문자로 시작합니다. 인스턴스 메서드를 호출하려면 개체.인스턴스메서드이름()
형태를 사용합니다.
예를 들어, Car
클래스의 Go()
인스턴스 메서드를 호출하는 코드는 다음과 같습니다.
Car car = new Car();
car.Go();
위와 같이 개체이름.인스턴스메서드이름()
형태를 사용합니다.
인스턴스 메서드 만들기
클래스에 인스턴스 메서드를 만들고 호출하는 내용을 예제로 살펴보겠습니다. 인스턴스 메서드는 static
키워드가 붙지 않는 메서드입니다.
코드: ClassMethod.cs
// ClassMethod.cs
> //[?] 인스턴스 메서드 만들기
> public class MyMath
. {
. //[1] 인스턴스 메서드 생성
. public void Sum(int x, int y)
. {
. int sum = x + y;
. Console.WriteLine($"합계: {sum}");
. }
. }
>
> //[2] MyMath 클래스의 인스턴스 생성
> MyMath myMath = new MyMath();
>
> //[3] 개체.인스턴스메서드이름 형태로 호출
> myMath.Sum(3, 5);
합계: 8
[1]
번 코드처럼 static
이 빠진 형태로 클래스 내에 메서드를 만들면 이 메서드는 인스턴스 메서드가 됩니다. 인스턴스 메서드를 호출하려면 [2]
번 코드처럼 new
키워드를 사용하여 인스턴스를 생성해야 합니다. [2]
번 코드는 다음과 같이 var
키워드를 사용해도 됩니다.
> var myMath = new MyMath();
[3]
번 코드에서 볼 수 있는 것처럼 인스턴스 멤버들은 개체.인스턴스메서드이름
형태로 호출이 됩니다.
익명 형식(Anonymous Type)
클래스 선언없이 개체를 만드는 방법인 익명 형식(Anonymous Type)도 있습니다. 익명 형식은 무명 형식 또는 이름이 없는 개체로도 표현합니다. 다음 코드를 살펴보세요.
코드: ObjectDemo.cs
// ObjectDemo.cs
> //[?] 익명 형식(Anonymous Type): 클래스없이 개체를 만드는 방법
> //[1] 개체(Object) 만들기: 익명 형식(Anonymous Type)
> var hong = new { Name = "홍길동", Age = 21 };
> var park = new { Name = "박문수", Age = 30 };
>
> //[2] 개체 사용하기
> $"이름: {hong.Name}, 나이: {hong.Age}"
"이름: 홍길동, 나이: 21"
> $"이름: {park.Name}, 나이: {park.Age}"
"이름: 박문수, 나이: 30"
익명 형식은 특정 클래스없이 하나의 이름으로 여러 속성들을 모아서 관리할 때 유용하게 사용될 수 있습니다.
익명 형식을 만들 때 각각의 데이터 형식은 자동으로 유추되어 만들어집니다. 다음 코드의 IsPrint
는 true
로 초기화되어 자동으로 불(bool) 형식의 데이터가 됩니다.
> var o = new { Id = 1, Note = "Anonymous Type", IsPrint = true };
> if (o.IsPrint)
. {
. Console.WriteLine($"{o.Id} - {o.Note}");
. }
1 - Anonymous Type
정적(Static) 멤버와 인스턴스(Instance) 멤버
계속 반복해서 설명하는 내용이지만, 클래스 내에서 선언되는 모든 멤버는 두 가지 유형 중에서 하나를 가질 수 있는데, static
키워드가 멤버 선언시 붙느냐(정적 멤버), 안 붙느냐(인스턴스 멤버)로 구분 합니다. static
키워드가 붙은 멤버에 접근하고자 할 때에는 클래스이름.멤버이름
과 같이 접근하고, static
키워드가 붙지 않은 멤버에 접근하고자 할 때에는 클래스의 인스턴스를 먼저 생성하고, 생성된 개체이름.멤버이름
으로 접근합니다.
static
키워드가 붙은 변수를 클래스 변수
라고 하고 static
붙지 않은 변수를 인스턴스 변수
라고 합니다. static
키워드가 붙으면 일반적으로 공유(Shared)의 개념을 가지는데 클래스내의 여러 메서드에서 해당 클래스 변수를 공유해서 사용합니다. 이러한 static
키워드가 붙는 메서드는 정적 메서드로 클래스의 인스턴스를 생성하지 않고 바로 사용할 수 있는 메서드입니다. 정적 메서드를 포함한 정적 멤버들은 모두 정적인 멤버만 호출 할 수 있습니다.
정적 멤버와 인스턴스 멤버 사용하기
이번에는 정적 멤버와 인스턴스 멤버를 함께 사용하는 예제를 살펴보겠습니다.
코드: StaticAndInstance.cs
// StaticAndInstance.cs
> //[?] 정적(Static, Shared) 멤버와 인스턴스(Instance) 멤버
> //[1] 클래스 생성
> class SharedAndInstance
. {
. //[1][1] static(shared) 멤버
. public static void StaticMember() => Console.WriteLine("[1] Static Member");
. //[1][2] instance 멤버
. public void InstanceMember() => Console.WriteLine("[2] Instance Member");
. }
>
> //[2] 클래스 사용
> //[2][1] 정적 멤버 사용
> SharedAndInstance.StaticMember(); // 정적 멤버 => 클래스.멤버 형태
[1] Static Member
>
> //[2][2] 인스턴스 멤버 사용
> SharedAndInstance obj = new SharedAndInstance();
> obj.InstanceMember(); // 인스턴스 멤버 => 개체.멤버 형태
[2] Instance Member
클래스에 메서드와 같은 클래스 멤버를 만들 때 처음에 가장 먼저 확인해야할 내용은 정적 멤버인지 인스턴스 멤버인지를 구분해야 합니다. 정적 멤버는 static
키워드가 붙은 멤버로 클래스내에서 공유해서 사용됩니다. 인스턴스 멤버는 static
키워드가 없이 인스턴스를 생성한 후 호출하는 형태입니다. 일반적으로 닷넷 프로그래밍에서는 인스턴스 멤버가 월등히 많이 사용됩니다.
[실습] 프로젝트에 클래스 여러 개 사용하기
소개
지금까지 만들었던 모든 C# 프로젝트에는 한 개 이상의 cs 파일이 오고 해당 cs 파일에는 역시 한 개 이상의 클래스 파일이 올 수 있습니다. 이번 실습을 통해서 여러 개의 클래스를 생성하고 사용하는 방법을 알아보겠습니다.
따라하기: 2개의 클래스를 만들고 Main() 메서드에서 호출해서 사용하기
(1) 새로운 C# 콘솔 프로젝트를 다음과 같이 생성합니다. 참고로, 다음 경로의 책 및 강의 소스의 DotNet – 34_Object – ClassDemo 폴더에 각각의 소스가 있습니다.
프로젝트 형식 | 템플릿 이름 | 위치 |
---|---|---|
Visual C# 콘솔 응용 프로그램 | ClassDemo | C:\C# |
(2) 솔루션 탐색기에서 ClassDemo
프로젝트에 마우스 오른쪽 버튼을 클릭 <추가 > 새 항목>
을 클릭하여 템플릿 선택 화면에서 클래스를 선택하고 이름은 ClassOne.cs
로 설정한 후 [추가]
버튼을 클릭하여 프로젝트에 추가합니다. 동일 절차로 ClassTwo.cs
파일을 추가합니다.
Visual Studio에 설치되어 있는 템플릿 | 이름 |
---|---|
클래스 | ClassOne.cs |
클래스 | ClassTwo.cs |
(3) 새롭게 추가한 클래스 파일인 ClassOne.cs
파일에 다음과 같이 소스 코드를 작성합니다.
코드: ClassOne.cs
// ClassOne.cs
using System;
//[1] ClassOne 클래스
public class ClassOne
{
// 정적(static) 메서드: 클래스명.메서드명();
public static void Hi()
{
Console.WriteLine("안녕하세요.");
}
}
Hi()
메서드에는 static
키워드를 붙여 정적 메서드로 만들었습니다.
(4) 새롭게 추가한 클래스 파일인 ClassTwo.cs
파일에 다음과 같이 소스 코드를 작성합니다.
코드: ClassTwo.cs
// ClassTwo.cs
using System;
//[2] ClassTwo 클래스
public class ClassTwo
{
// 정적(static) 멤버
public static void Hi()
{
Console.WriteLine("반갑습니다.");
}
// 인스턴스(instance) 멤버: static 없는 인스턴스 메서드
public void Hello()
{
Console.WriteLine("또 만나요.");
}
}
Hi()
메서드는 정적 메서드로 만들었고 이와 다른 인스턴스 메서드를 비교하기 위해서 Hello()
메서드는 static
키워드를 생략하여 인스턴스 메서드로 만들었습니다.
(5) 솔루션 탐색기에서 Program.cs 파일을 ClassDemo.cs
파일로 이름을 변경한 후 이미 만들어져 있는 모든 코드를 삭제한 후 다음과 같이 프로그램을 작성합니다.
코드: ClassDemo.cs
// ClassDemo.cs
//[!] 메인 클래스: ClassOne과 ClassTwo 클래스를 사용
class ClassDemo
{
static void Main()
{
//[!] 다른 클래스의 멤버 호출
//[a] 스태틱 멤버 호출
ClassOne.Hi(); // "안녕하세요." 출력
ClassTwo.Hi(); // "반갑습니다." 출력
//[b] 인스턴스 멤버 호출: 클래스의 인스턴스 생성 => 개체(객체)
ClassTwo ct = new ClassTwo();
ct.Hello(); // "또 만나요." 출력
}
}
각각의 클래스에 만들어진 정적 메서드들은 클래스이름.메서드이름()
형태로 호출합니다. 또한 각각의 클래스에 만들어진 인스턴스 메서드들은 new
키워드를 통해서 클래스의 인스턴스인 개체를 만들고 이 개체를 통해서 호출합니다.
(6) 소스 코드를 다 입력한 후 [Ctrl]+[F5]
를 눌러 프로그램을 실행하면 명령 프롬프트 창에 다음과 같이 출력됩니다.
안녕하세요.
반갑습니다.
또 만나요.
마무리
책에서는 지면 절약을 위해, 하나의 코드에 여러 클래스 파일을 둡니다. 앞으로 이루어지는 현업 단계의 개발에서는 하나의 프로젝트에 여러 개의 클래스를 추가하여 하나의 프로그램(프로젝트)을 이루는 방식이 자주 사용될 것입니다. 여러 개의 클래스들 간의 관계와 호출에 대한 개념은 앞으로 이어지는 클래스의 구성요소를 통해서 계속해서 알아보도록 하겠습니다.
ToString() 메서드 오버라이드
클래스에는 ToString()
이름의 특별한 메서드를 생성할 수 있습니다. 이를 사용하면 개체에 대한 문자열을 재정의할 수 있습니다. 이러한 기능을 ToString 메서드 오버라이드
(다시 정의)라고 합니다.
코드: ToStringMethod.cs
// ToStringMethod.cs
using System;
class My { }
class Your
{
//[!] ToString() 메서드를 다시 정의하여 새로운 문자열 출력
public override string ToString()
{
return "새로운 반환 문자열 지정";
}
}
class ToStringMethod
{
static void Main()
{
My my = new My();
Console.WriteLine(my); // "My": 개체를 출력하면 기본은 클래스 이름이 출력
Your your = new Your();
Console.WriteLine(your); // "새로운 반환 문자열 지정"
}
}
My
새로운 반환 문자열 지정
클래스를 만들 때 특별히 public override string ToString() {}
메서드를 구현하고 문자열을 반환하면 이는 기본 개체를 출력할 때 그에 해당하는 문자열을 재정의하여 출력할 수 있습니다.
My
클래스는 따로 ToString
메서드가 만들어지지 않아 클래스 이름이 출력되고 Your
클래스는 새로운 문자열로 출력됩니다.
클래스 배열
우리가 만든 클래스도 하나의 데이터 형식이기에 배열처럼 사용할 수 있습니다.
코드: ClassArray.cs
// ClassArray.cs
// 특정 클래스 형식의 배열을 선언 후 각 배열의 인스턴스 생성 후 사용
using System;
public class CategoryClass
{
public void Print(int i) => Console.WriteLine($"카테고리 {i}");
}
class ClassArray
{
static void Main()
{
//[1] 클래스 배열 생성
CategoryClass[] categories = new CategoryClass[3];
//[2] 각각의 요소에 인스턴스 생성
categories[0] = new CategoryClass();
categories[1] = new CategoryClass();
categories[2] = new CategoryClass();
for (int i = 0; i < categories.Length; i++)
{
categories[i].Print(i);
}
}
}
카테고리 0
카테고리 1
카테고리 2
[1]
번 코드와 같이 클래스도 기본 데이터 형식처럼 배열을 선언할 수 있습니다. 각각의 배열 요소는 [2]
번 코드처럼 새롭게 인스턴스로 설정해서 사용할 수 있습니다.
var
키워드를 사용하여 클래스의 인스턴스 생성
클래스의 인스턴스를 생성할 때 var
키워드를 사용하면 코드가 약간 짧아집니다. 다음 코드는 Exam exam1
대신 var
를 사용하여 var exam2
형태로 클래스 자리에 변수를 선언하고, 뒤에 오는 클래스 생성 구문을 참고해 자동으로 클래스 이름을 유추해서 개체를 생성합니다. var
키워드를 사용하여 클래스의 인스턴스를 생성하는 방법을 살펴보세요.
코드: InstanceWithVar.cs
// InstanceWithVar.cs
> public class ExamClass { }
> //[1] 클래스를 사용하기 위해 인스턴스 생성
> ExamClass exam1 = new ExamClass(); // 기본 방식
> $"{exam1}"
"Submission#10+ExamClass"
> //[2] var 키워드를 사용하여 인스턴스 생성
> var exam2 = new ExamClass(); // 축약 방식
> $"{exam2}"
"Submission#10+ExamClass"
코드에서는 var
키워드를 생성했지만, 실제 컴파일했을 때에는 var
자리를 Exam
클래스가 대체한다고 보면 됩니다. 이번 예제에서는 Exam
처럼 클래스 이름이 길지 않기에 크게 상관없지만 긴 클래스 이름을 사용할 때는 간단히 var
로 줄여서 표현하는 것도 나쁘지 않습니다.
요약
간단히 예를 들자면 정적 멤버 호출은 가내수공업으로 필요할 때 바로 호출해서 사용하는 개념이고, 인스턴스 멤버 호출은 대기업의 기성품처럼 설계도로부터 대량으로 개체를 만들어 사용하는 형태입니다. 프로그램 내에서 한두 번 호출하는 경우에는 정적 멤버를, 여러 번 반복해서 사용하는 경우에는 인스턴스 멤버를 사용합니다. 클래스와 개체에 대한 개념을 다루었으니 다음 강의에서 클래스의 구성 요소들을 좀 더 학습해보겠습니다.