클래스 사용하기: 클래스는 개체를 만들어내는 틀

  • 56 minutes to read

C#의 모든 코드에 반드시 들어가는 class의 주요 기능에 대해서 알아보는 시간을 갖도록 하겠습니다.

> // 클래스(Class): 닷넷의 공용 형식 시스템의 기본 구문 중 하나로 데이터와 함수를 묶어 관리

1. 클래스(Class) 소개

C#에서의 클래스는 지금껏 작성해 본 모든 코드의 기본 코드로 작성되는 C#의 핵심 코드입니다. public class 클래스명{}과 같은 코드 블록을 사용하여 클래스를 정의할 수 있습니다. 클래스를 정의하는 전반적인 내용과 클래스의 내부 또는 외부에 올 수 있는 구성 요소는 다음 장에서 살펴보기로 하고, 이번에는 이미 닷넷 프레임워크에 만들어져있는 유용한 내장 클래스의 사용법에 대해서 살펴보기로 합니다.

클래스의 구성요소는 많이 있지만 가장 많이 사용되는 것은 속성과 메서드입니다. 속성은 데이터를 다루고 메서드는 로직을 다룹니다.

  • 클래스
    • 속성: 데이터
    • 메서드: 로직

클래스는 그 의미에 따라서 이미 닷넷 프레임워크에 만들어 있는 내장 형식(Built-In Type)과 사용자가 직접 클래스의 구조를 만드는 사용자 정의 형식(User Defined Type)으로 구분할 수 있습니다.

예를 들어 Console, String, Math 등의 클래스는 내장 형식이고 Product, Note, User, Group처럼 새로운 형식을 정의할 수 있는데, 이를 사용자 데이터 형식이라고 합니다.

2. 클래스(Class) 만들기

클래스를 한 줄로 정의하면 다음과 같습니다.

  • 클래스는 개체를 생성하는 틀(템플릿)입니다.
  • 클래스는 무언인가를 만ㄷ르어 내는 설계도입니다.

클래스(Class)는 C# 프로그래밍의 기본 단위로 새로운 개체(실체)를 생성하는 설계도(청사진) 역할을 합니다. 실생활에서의 예를 들면 자동차라는 개체(Object)를 만들어낼 수 있는 자동차 설계도 역할을 하는 것이 프로그래밍에서 바로 클래스(Class)입니다.

이처럼 클래스는 개체를 생성하기 위한 틀(템플릿)입니다. 일반적으로 클래스를 설명할 때 가장 많이 사용하는 용어가 청사진(Blue Print)입니다. 이처럼, 클래스는 무엇인가를 만들어내는 설계도입니다.

클래스는 지금까지 우리가 사용해 온 class 키워드를 사용하여 다음과 같이 만듭니다.

> class MyClass { }

이때 클래스 이름은 메서드 등의 이름과 마찬가지로 반드시 대문자로 시작합니다.

C#에서의 클래스에 대한 추가 내용은 다음과 같습니다. 간단히 읽고 넘어갑니다.

  • 클래스는 C# 프로그램의 기본 구조(골격)입니다.
  • 새로운 데이터 형식(Data Type)을 만들 수 있는 개념으로 사용됩니다.
  • 영어로 Thing의 의미로 물건 또는 사물을 표현할 때 사용됩니다.
  • 새로운 개체(Object)를 만들어내는 템플릿, 틀(Mould)의 의미를 갖습니다.

클래스 선언(클래스 이름은 대문자로 시작)

클래스를 선언하는 모양은 다음과 같습니다.

> public class 클래스이름
. {
.     // 클래스 내용 구현
. }

클래스 선언 시 사용하는 public 키워드 또는 internal 키워드는 지금까지는 생략하고 사용했지만, 여러 개의 클래스를 사용할 때에는 public 키워드가 사용될 것입니다. public 키워드가 붙은 클래스는 클래스 외부에서 해당 클래스를 바로 호출해서 사용할 수 있다는 공개의 의미입니다. 반대의 의미는 private 키워드를 사용합니다.

클래스 레벨의 메서드 호출하기

클래스에 메서드를 하나 만들고 클래스.메서드() 형태로 호출하는 내용을 먼저 살펴보겠습니다. 편집기를 열고 다음과 같이 작성 후 실행해 보세요.

코드: ClassNote.cs

using System;

class ClassNote
{
    static void Run()
    {
        Console.WriteLine("ClassNote 클래스의 Run 메서드");
    }

    static void Main()
    {
        Run(); //[1] 메서드 레벨: 같은 클래스의 메서드 호출
        ClassNote.Run(); //[2] 클래스 레벨: 클래스.메서드() 형태로 호출
    }
}
ClassNote 클래스의 Run 메서드
ClassNote 클래스의 Run 메서드

[1]번 코드는 지금까지 우리가 사용해오던 방식입니다. 이때 Main 메서드와 동일한 형태(static 키워드 사용)의 Run 메서드를 만들고 이를 [2]번 코드 형태처럼 클래스.메서드 형태인 ClassNote.Run() 코드로 Run() 메서드를 클래스 레벨에서 호출할 수 있습니다. 같은 클래스의 메서드를 호출할 때에는 [1]번 방식을 사용하지만, 다른 클래스에 있는 메서드는 [2]번과 같은 형태로 호출해서 사용할 수 있습니다.

정적(Static)과 정적 메서드

C#에서는 static을 정적으로 표현합니다. 말이 조금 어려울 수 있는데요. 이는 의미가 같은 다른 말로 표현하면 공유(Shared)입니다. static이 붙는 클래스의 모든 멤버들은 해당 클래스내에서 누구나 공유해서 접근 가능한 멤버가 됩니다.

메서드에 static이 붙는 메서드를 정적 메서드라고 하는데 이를 공유 메서드(Shared Method)라고도 부릅니다. 다음 코드는 Square 클래스에 정적 메서드인 GetName() 메서드를 만들고 이를 SquareClass 클래스의 Main() 메서드에서 Square.GetName()으로 호출해서 사용하는 내용을 보여줍니다.

코드: SquareClass.cs

> //[?] 클래스의 정적 멤버 호출
> //[1] Square.cs
> public class Square
. {
.     public static string GetName()
.     {
.         return "정사각형";
.     }
. }
> 
> //[2] SquareClass.cs 
> // Square 클래스의 정적(static) 멤버인 GetName() 메서드 호출
> string square = Square.GetName();
> square
"정사각형"

정적(Static)과 인스턴스(Instance)

.NET의 많은 API들처럼 우리가 새롭게 만드는 클래스는 각각의 멤버에 static 키워드를 유무에 따라서 정적 또는 인스턴스 멤버가 될 수 있습니다. 앞으로 계속해서 다룰 내용인데요. 우선, 다음 예제를 먼저 실행해보세요.

코드: MyFirstClass.cs

using System;

class MyFirstClass
{
    static void StaticMethod() => Console.WriteLine("[1] 정적 메서드");

    void InstanceMethod() => Console.WriteLine("[2] 인스턴스 메서드");

    static void Main()
    {
        //[1] 정적 메서드 호출
        MyFirstClass.StaticMethod();
        //[2] 인스턴스 메서드 호출: new 키워드로 새로운 개체 생성하는 코드 모양
        MyFirstClass my = new MyFirstClass();
        my.InstanceMethod(); 
    }
}
[1] 정적 메서드
[2] 인스턴스 메서드

[1]번 코드는 static이 붙은 정적 메서드를 클래스.메서드() 형태로 호출하는 내용입니다. 정적 메서드는 클래스에서 공유해서 사용하기에 공유 메서드로도 불립니다. [2]번 코드는 static이 붙지 않은 인스턴스 멤버를 클래스의 인스턴스 생성 후 해당 인스턴스 개체를 통해서 호출하는 내용입니다. 이처럼 클래스내의 멤버를 구성할 때 static이 붙으면 정적 멤버가 되고 static이 붙지 않으면 인스턴스 멤버가 됩니다.

여러 개의 클래스 만들기

이번에는 하나의 C# 파일에 하나 이상의 클래스 파일을 만들어 보겠습니다. 편집기를 열고 다음과 같이 작성 후 실행해 보세요. 새롭게 만들어진 클래스와 메서드에 public 키워드가 붙은 걸 주의 깊게 살펴보세요.

코드: ClassDescription.cs

using System;

public class MyClass
{
    public static void MyMethod()
    {
        Console.WriteLine("클래스");
    }
}

class ClassDescription
{
    static void Main()
    {
        MyClass.MyMethod(); // 클래스명.메서드명()
    }
}
클래스

지금까지 우리는 하나의 CS 파일에 하나의 클래스만 만들고 사용했습니다. 위 예제에서는 하나의 CS 파일에 MyClassClassDescription 클래스 2개를 만들어 봤습니다. Main() 메서드를 포함하는 ClassDescription 클래스와 새롭게 MyClass 이름으로 클래스를 만들고 해당 클래스에 MyMethod() 이름으로 메서드를 만들어 보았습니다. 메서드 시그니처는 Main() 메서드와 동일한 구조에 특별히 public을 붙였습니다. 메서드 또는 클래스를 만들 때 public 키워드를 사용하면 해당 클래스 또는 메서드에 대해서 접근 권한을 설정할 수 있는데, 공용(Public)의 의미로 외부에서 접근할 수 있다는 의미입니다. 즉, 외부에서 접근할 수 있는 클래스 또는 메서드에는 public 키워드를 붙여줘야 합니다. 이러한 기능을 접근(액세스) 한정자라고 합니다. public 키워드가 있고 Main() 메서드처럼 static이 붙은 MyMethod() 메서드를 호출할 때에는 클래스명.메서드명() 형태로 호출할 수 있습니다. Main() 메서드와 동일하게 static 키워드를 붙여진 메서드이기에 바로 클래스 이름 뒤에 점(.)을 붙여서 정적(Static) 호출을 할 수 있습니다. C#에서 정적 메서드를 호출하는 방법은 클래스이름.메서드이름() 형태를 사용합니다. 정적 메서드를 만드려면 메서드를 정의할 때 static 키워드를 붙입니다. 참고로, 프로그램 빈도상 static을 안붙이는 멤버가 더 많이 사용됩니다. ClassDescription 클래스와 Main() 메서드도 앞에 public 키워드를 붙여도 됩니다. 여러 클래스 단위로 학습할 때에는 public 키워드가 기본 구조로 보면 됩니다.

클래스 시그니처

클래스는 다음과 같은 시그니처를 갖습니다.

> public class Car { }

public 액세스 한정자를 생략하게 되면 기본값인 internal을 갖게 되는데 이는 해당 프로그램내에서 언제든 접근 가능한 구조입니다. 하지만, 학습 단계에서는 클래스는 public만을 사용해도 무관합니다.

class 키워드 다음에 클래스이름이 오는데 클래스 이름은 대문자로 시작하는 명사를 사용합니다. 클래스의 본문 또는 몸통(바디, Class Body)을 표현하는 중괄호 안에는 지금까지 배운 메서드와 앞으로 다룰 필드, 속성, 생성자, 소멸자 등의 기능들이 들어오는데 이 모두를 가리켜 클래스 멤버라고 부릅니다.

클래스 이름 짓기

클래스의 이름은 의미 있는 이름 사용하고 명사를 사용합니다. 그리고 첫자 대문자는 꼭 지켜야 합니다. 또한 클래스 이름을 지을 때에는 축약형 쓰지 말고 특정 접두사 쓰지 않고 언더스코어와 같은 특수 문자를 쓰지 않습니다.

클래스의 주요 구성 요소들

클래스의 시작과 끝, 즉, 클래스의 블록 안에는 다음과 같은 용어(개념)들이 포함될 수 있습니다. 클래스의 구성 요소를 가리킬 때 클래스의 멤버(Member)란 용어와 혼용해서 사용합니다. 다음 내용은 앞으로 자세히 다룰 예정이니 간단히 읽어보고 넘어갑니다.

  • 필드(Field): 클래스(개체)의 부품 역할, 클래스 내에 선언된 변수, 데이터를 담는 그릇, 개체 상태 저장
  • 메서드(Method): 개체의 동작/기능 정의, 기능 수행
  • 생성자(Constructor): 개체의 필드 초기화, 개체 생성할 때 미리 수행되는 기능 정의
  • 소멸자(Destructor): 개체가 다 사용된 후 메모리에서 소멸될 때 실행
  • 속성(Property): 개체의 색상, 크기, 모양 등을 정의

액세스 한정자

클래스를 생성할 때 public, private, internal, abstract, sealed와 같은 키워드를 붙일 수 있습니다. 이를 액세스 한정자(Access Modifier)라고 합니다. 액세스 한정자는 클래스에 접근할 수 있는 범위를 결정하는데 도움이 됩니다. 일반적으로 이 강의에서는 특별한 지정을 하지 않는 한 public 액세스 한정자를 기본으로 사용합니다.

클래스와 멤버

이번에는 클래스를 만들고 메서드 멤버를 생성하는 방법을 살펴보겠습니다.

코드: ClassAndMember.cs

using System;

// [1][1] 클래스 생성
public class ClassName
{
    // [1][2] 멤버 생성: 메서드 멤버 생성
    public static void MemberName()
    {
        Console.WriteLine("클래스의 멤버가 호출되어 실행됩니다.");
    }
}

public class ClassAndMember
{
    public static void Main()
    {
        // [2][1] 클래스 사용
        ClassName.MemberName(); // 정적(Static) 멤버 접근
    }
}
클래스의 멤버가 호출되어 실행됩니다.

Main() 메서드와 Main() 메서드를 포함하고 있는 클래스도 public 액세스 한정자를 붙여 보았습니다. 새롭게 생성한 ClassName의 클래스에 MemberName() 이름으로 메서드를 만들어 보았습니다. 클래스의 멤버 중에 static 키워드가 붙는 멤버들은 모두 정적인 멤버로 클래스명.멤버명() 형태로 호출됩니다. C#에서 메서드는 static이 붙은 정적 메서드와 그렇지 않고 개체의 인스턴스를 생성하여 사용하는 인스턴스 메서드로 나뉩니다.

자주 사용되는 내장 클래스

우리가 직접 만드는 클래스는 앞으로 계속해서 살펴볼 예정입니다. 우선은 닷넷에서 기본으로 제공되는 클래스들을 사용해 보는 시간을 먼저 갖도록 하겠습니다. 즉, 사용법을 먼저 충분히 익히고 그러한 기능을 직접 만들어보는 순서대로 학습해 나가겠습니다.

C# 기반에서 주로 사용되는 내장 클래스(BuiltIn Class)는 상당히 많지만 시작하는 단계에서는 아래만 제시하고 나중에 나오는 클래스들은 그때마다 정리를 하면서 학습하기로 합니다.

  • String 클래스: 프로그램 작성시 가장 많이 사용되는 문자열 처리 관련된 속성 및 메서드를 풍부하게 제공합니다.
  • StringBuilder 클래스: 대용량 문자열 처리 관련된 속성 및 메서드를 제공, 일반적인 경우에는 string을 사용하여 문자열을 저장하고 큰 규모의 문자열 저장이 필요한 경우에는 StringBuilder 클래스 사용을 권장합니다. StringBuilder 클래스는 System.Text 네임스페이스에 들어 있습니다.
  • Array 클래스: 배열 관련된 주요 속성 및 메서드를 제공합니다.

Environment 클래스를 사용해 프로그램 강제 종료하기

우선 닷넷 프레임워크에 내장된 간단한 클래스부터 살펴보겠습니다. Environment 클래스의 Exit() 메서드를 사용하면 콘솔 프로그램을 강제로 종료할 수 있습니다.

코드: EnvironmentExit.cs

using System;

class EnvironmentExit
{
    static void Main()
    {
        Console.WriteLine("출력됩니다.");

        Environment.Exit(0);

        Console.WriteLine("출력될까요?");
    }
}
출력됩니다.

System 네임스페이스에 있는 Enviroment 클래스의 Exit() 메서드에 0 값을 매개 변수로 전달하면 현재 프로그램을 종료합니다. 이 코드 이후로 아무리 많은 코드가 와도 실행되지 않습니다.

환경 변수(Environment) 사용하기

이번에는 Environment 클래스의 여러 가지 속성을 사용하여 환경 변수를 출력해보겠습니다.

코드: EnvironmentDemo.cs

> //[?] Environment 클래스
> Environment.SystemDirectory
"C:\\WINDOWS\\system32"
> Environment.Version
[8.0.13]
> Environment.OSVersion
[Microsoft Windows NT 10.0.22000.0]
> Environment.MachineName
"VISUALACADEMY"
> Environment.UserName
"RedPlus"
> Environment.CurrentDirectory
"C:\\Users\\RedPlus"
> string docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
> Console.WriteLine(docs);
C:\Users\RedPlus\OneDrive - 하와소\문서
> 

Environment 클래스의 여러 가지 속성을 사용하여 컴퓨터의 환경 변수 값을 출력해 보았습니다. 이 환경 변수는 컴퓨터마다 서로 다른 값을 출력하므로 실행 결과는 다릅니다.

참고: C# 프로젝트의 bin 폴더와 obj 폴더

bin 폴더는 binary의 약자로, 실행 파일이 생성되는 폴더입니다.
obj 폴더는 컴파일 과정에서 최종 바이너리를 만들기 전에 생성되는 임시 파일과 기타 빌드 관련 파일을 저장하는 폴더입니다.
즉, bin 폴더에는 최종 실행 가능한 바이너리(어셈블리) 파일이 저장되며, obj 폴더는 빌드 과정에서 사용되는 중간 파일을 보관하는 역할을 합니다.

또 다른 exe 파일 실행하기

C#에서 이미 만들어져 있는 또 다른 실행 파일인 exe 파일을 실행할 수 있습니다. Process 클래스의 Start() 메서드를 사용하면 프로그램 코드 내에서 exe 파일을 실행할 수 있습니다.

다음 코드를 작성 후 실행해보세요. 참고로 이번 코드는 Windows 운영체제 환경에서 실행한 내용입니다.

코드: ProcessStartDemo.cs

using System.Diagnostics;

namespace DotNet
{
    class ProcessStartDemo
    {
        static void Main()
        {
            //[1] 메모장을 실행합니다. 
            Process.Start("Notepad.exe"); 
            //[2] 웹 브라우저를 열고 매개 변수로 URL을 절달합니다.
            Process.Start("Explorer.exe", "https://dotnetkorea.com"); 
        }
    }
}
위 코드를 실행하면 콘솔 창이 열림과 동시에 메모장이 실행되고 웹 브라우저도 실행됩니다. 이처럼 `System.Diagnostics` 네임스페이스에 있는 `Process` 클래스의 `Start()` 메서드를 사용하면 exe 실행 파일을 실행할 수 있습니다. 참고로 Notepad.exe와 Explorer.exe는 운영 체제에 따라서 실행되지 않을 수 있어 에러가 발생할 수 있습니다.  

실행 결과

추가적으로 Process 클래스의 많은 API 중에서 다음 코드를 C# 인터렉티브에서 실행하면 해당 프로세스 이름을 얻을 수 있습니다.

> System.Diagnostics.Process.GetCurrentProcess().ProcessName
"InteractiveHost64"

마찬가지로 C# 콘솔 응용 프로그램에서 다음 코드를 실행하면 해당 프로젝트의 실행 이름을 얻을 수 있습니다.

코드: ProcessGetCurrentProcess.cs

using System;
using System.Diagnostics;

class ProcessGetCurrentProcess
{
    static void Main()
    {
        Console.WriteLine(Process.GetCurrentProcess().ProcessName);
    }
}

Random 클래스

랜덤(Random)은 임의의 값을 나타냅니다. 숫자로 따지면 임의의 수인 난수를 의미합니다. C#에서는 난수를 발생시키는 기능을 Random 클래스를 통해서 구현할 수 있습니다. 랜덤 클래스를 사용하려면 랜덤 클래스의 인스턴스(Instance)를 생성해야 합니다. 다음 코드와 같이 Random 클래스의 인스턴스를 생성하여 random 개체(Object)를 만든 후 Next() 메서드를 호출하여 난수를 얻을 수 있습니다. 클래스(Class), 개체(Object), 인스턴스(Instance)의 3가지 단어는 앞으로도 계속해서 알아볼 테니 지금은 코드를 작성 후 실행해보는 식으로 넘어가면 됩니다.

> Random random = new Random();
> random.Next()
1038741625

Random 클래스를 사용하여 임의의 정수 출력하기

프로젝트 기반으로 Random 클래스를 사용해보겠습니다.

코드: RandomDemo.cs

using System;

class RandomDemo
{
    static void Main()
    {
        // Random 클래스의 인스턴스 생성
        Random random = new Random();

        Console.WriteLine(random.Next());       // 임의의 정수
        Console.WriteLine(random.Next(5));      // 0 ~ 4
        Console.WriteLine(random.Next(1, 10));  // 1 ~ 10
    }
}
14286524
4
7

위 실행 결과는 매번 실행할 때마다 다르게 나옵니다. Next() 메서드는 정수를 NextDouble() 메서드는 실수를 반환합니다.

로또 번호 생성기 만들기

이번에는 간단한 로또 번호 생성기를 만들어 보겠습니다.

코드: RandomClassDemo.cs

using System;

class RandomClassDemo
{
    static void Main()
    {
        Console.Write("이번 주의 로또: ");
        Random ran = new Random();
        int[] arr = new int[6]; // 6개 데이터
        int temp = 0;
        for (int i = 0; i < 6; i++)
        {
            temp = ran.Next(45) + 1; // 1 ~ 45까지
            bool flag = false;
            if (i > 0 && i < 6)
            {
                for (int j = 0; j <= i; j++)
                {
                    if (arr[j] == temp)
                    {
                        flag = true; // 중복되면, true로 설정        
                    }
                }
            }
            if (flag)
            {
                --i; // 중복되었다면, 현재 인덱스를 재 반복     
            }
            else
            {
                arr[i] = temp; // 중복된 데이터가 없다면, 저장
            }
        }
        for (int i = 0; i < 6; i++)
        {
            Console.Write("{0} ", arr[i]);
        }
        Console.WriteLine();
    }
}
이번 주의 로또: 18 17 37 30 32 31

가위 바위 보 게임

이번에는 간단한 가위 바위 보 게임 프로그램을 만들어보겠습니다.

코드: RockPaperScissors.cs

using System;

class RockPaperScissors
{
    static void Main()
    {
        int iRandom = 0; // 1(가위), 2(바위), 3(보)
        int iSelection = 0; // 사용자 입력(1~3)
        string[] choice = { "가위", "바위", "보" };

        // 컴퓨터의 랜덤값 지정
        iRandom = (new Random()).Next(1, 4);

        Console.Write("1(가위), 2(바위), 3(보) 입력 : _\b");
        iSelection = Convert.ToInt32(Console.ReadLine());

        Console.WriteLine("\n 사용자 : {0}", choice[iSelection - 1]);
        Console.WriteLine(" 컴퓨터 : {0}\n", choice[iRandom - 1]);

        // 결과 출력
        if (iSelection == iRandom)
        {
            Console.WriteLine("비김");
        }
        else
        {
            switch (iSelection)
            {
                case 1: Console.WriteLine((iRandom == 3) ? "승" : "패"); break;
                case 2: Console.WriteLine((iRandom == 1) ? "승" : "패"); break;
                case 3: Console.WriteLine((iRandom == 2) ? "승" : "패"); break;
            }
        }
    }
}
1(가위), 2(바위), 3(보) 입력 : 2

 사용자 : 바위
 컴퓨터 : 바위

비김

사용자로부터 1, 2, 3을 입력받아 이에 해당하는 랜덤 값과 비교를 해서 간단한 가위, 바위, 보 프로그램을 작성할 수 있습니다. 참고로, 위 프로그램에서는 1, 2, 3 이외의 값이 입력되면 에러가 발생하니 예외 처리 및 반복 등의 코드는 독자 스스로 업그레이드해보길 바랍니다.

프로그램 실행 시간 구하기

이번에는 프로그램 실행 시간을 계산하는 프로그램을 만들어보겠습니다.

코드: StopwatchDemo

// 프로그램명: StopwatchDemo
// Stopwatch 클래스를 사용하여 간단히 특정 프로세스(메서드, 로직)의 경과 시간을
// 밀리초 단위로 표시하는 기능을 구현하는 예제입니다.
using System;
using System.Diagnostics;
using System.Threading;

class StopwatchDemo
{
    static void Main()
    {
        // Stopwatch 클래스 : 특정 프로세스의 경과 시간(Elapsed)을 구하는 기능
        Stopwatch timer = new Stopwatch();
        timer.Start();
        LongTimeProcess();
        timer.Stop();

        // 밀리초 단위로 표시
        Console.WriteLine("경과 시간: {0}밀리초", timer.Elapsed.TotalMilliseconds); 
        // 초 단위로 표시 
        Console.WriteLine("경과 시간: {0}초", timer.Elapsed.Seconds);
    }

    static void LongTimeProcess()
    {
        // 3초간 대기: Thread.Sleep() 메서드로 현재 프로그램 3초간 대기
        Thread.Sleep(3000);
    }
}
경과 시간: 3000.6727밀리초
경과 시간: 3초

Stopwatch 클래스는 Start() 메서드와 Stop() 메서드를 제공하여 프로그램의 실행 시간을 잴 수 있습니다. 실행시간은 Elapsed 속성의 TotalMillisecondsSeconds와 같은 속성을 사용할 수 있습니다.

정규식(Regular Expression)

모든 프로그래밍 언어는 정규식(Regular Expression)을 지원합니다. 정규식은 간단히 말해 문자열에서 특정 패턴을 찾아주는 기능입니다. 정규식에 대한 내용은 따로 설명하진 않고 2개 정도의 예제를 작성 후 실행해 보는 형태로 넘어가도록 하겠습니다. 정규식에 대해 좀 더 궁금하신 독자분들은 관련 서적을 보시거나 인터넷의 공개된 자료를 살펴보시는 것을 권장합니다.

정규식으로 하나 이상의 공백을 하나로 변경하기

정규식을 사용하여 하나 이상의 공백을 공백 하나로 치환하는 내용은 다음 소스처럼 Regex 클래스의 힘을 빌려 손쉽게 구현이 가능합니다.

코드: RegexReplace.cs

using System;
using System.Text.RegularExpressions;

class RegexReplace
{
    static void Main()
    {
        string s = "안녕하세요.    반갑습니다.    또 만나요.";
        var regex = new Regex("\\s+"); // 하나 이상의 공백 패턴 검사
        string r = regex.Replace(s, " "); // 하나 이상의 공백을 공백 하나로 변환
        Console.WriteLine(s);
        Console.WriteLine(r);
    }
}
안녕하세요.    반갑습니다.    또 만나요.
안녕하세요. 반갑습니다. 또 만나요.

Regex 클래스의 생성자에 전달된 \s 기호는 공백 문자를 의미합니다. 즉, 하나 이상의 공백 문자를 검사해서 공백 하나로 변환하는 샘플 코드입니다.

정규식을 사용하여 이메일 형태인지 검증하기

이번에는 정규식을 사용하여 특정 문자열이 이메일(Email)의 형태인지 아닌지를 검사하는 내용을 살펴 보겠습니다.

코드: RegexDemo.cs

using System;
using System.Text.RegularExpressions;

class RegexDemo
{
    static void Main()
    {
        // 정규 표현식 관련 클래스: Regex
        string email = "abcd@aaa.com";
        Console.WriteLine(IsEmail(email));
    }

    static bool IsEmail(string email)
    {
        bool result = false;

        //[!] 이메일을 검사하는 정규식은 인터넷에서 검색하여 사용 가능
        Regex regex = new Regex(
            @"^[A-Za-z0-9](([_\.\-]?[a-zA-Z0-9]+)*)@([A-Za-z0-9]+)" +
            @"(([\.\-]?[a-zA-Z0-9]+)*)\.([A-Za-z]{2,})$");

        result = regex.IsMatch(email); // 패턴이 맞으면 True

        return result;
    }
}
True

[!] 주석 부분에 설명했듯이 이메일인지 확인하거나 홈페이지, 전화번호, 사업자번호 등등의 특정 패턴에 대한 정규식은 인터넷을 검색해 보면 많은 샘플이 나옵니다. 정규식 자체를 만드는 것으로 접근하는 것보다는 일단은 닷넷 API를 사용해보는 것처럼 정규식도 검색을 통해서 사용을 먼저 해보는 방식으로 학습하는 것을 권장합니다.

닷넷(.NET)의 방대한 API

C#에서는 수많은 구조체, 클래스, 그리고 다양한 메서드를 사용할 수 있으며, 실제로 활용해 본 것보다 써 보지 않은 기능이 훨씬 더 많을 정도로 방대한 API를 제공합니다. 닷넷(.NET)은 개발자가 다양한 프로그래밍 요구 사항을 해결할 수 있도록 광범위한 기능을 포함하고 있으며, 이를 통해 거의 모든 개발 시나리오를 처리할 수 있습니다.

C# 입문 강의를 학습하는 목적은 이러한 방대한 API 중에서 핵심적인 기능을 우선 익히고, 필요에 따라 추가적인 명령어를 학습할 수 있도록 하는 데 있습니다. 강의를 통해 기본적인 개념과 주요 API를 익히고 나면, 점차 스스로 필요한 기능을 찾아 활용하는 능력을 갖추게 됩니다.

우선은 강의나 책에서 다루는 기본적인 내용을 충실히 학습한 후, 프로젝트를 진행하면서 필요한 API를 탐색하고 적용해 나가는 방식이 가장 효율적인 학습 방법입니다.

값 형식(Value Type)과 참조 형식(Reference Type)

클래스와 구조체와 같은 데이터 형식을 말할 때 값 형식(Value Type)과 참조 형식(Reference Type)으로 구분짓기도 합니다.

  • 값 형식
    • 지금까지 다룬 int, double과 같은 데이터 구조는 내부적으로 구조체로 되어 있고 전형적인 값 형식의 데이터 구조입니다.
  • 참조 형식
    • 모든 클래스의 부모 역할을 하는 최상위 클래스인 Object 클래스와 이를 나타내는 object 키워드는 전형적인 참조 형식의 데이터 구조입니다.

값 형식(Value Type)

값 형식은 개체에 값 자체를 담고 있는 구조입니다.

그림: 값 형식

값 형식

참조 형식(Reference Type)

참조 형식은 개체가 값을 담고 있는 또 다른 개체를 포인터로 바라보는 구조입니다. 여러 값들이 동일한 개체를 가리킬 수 있습니다.

그림: 참조 형식

참조 형식

![INCLUDE 박싱과 언박싱]

is 연산자로 형식 비교하기

특정 개체가 특정 형식인지 검사할 때에는 is 연산자를 사용합니다. 개체.GetType() == typeof(형식) 형태의 줄임 표현으로 다음과 같이 사용됩니다. 참고로, 자바와 자바스크립트와 같은 언어의 instanceof 연산자와 동일한 의미로 사용됩니다.

  • 개체 is 형식
  • 변수 is 데이터 형식

특정한 값 또는 식에 대해서 특정 형식인지를 물어보는 연산자로 is 연산자의 결괏값은 불(불리언보다는 불로 표현) 값입니다.

다음 샘플 코드는 IsWhat 함수에 int, string, DateTime 형태를 넘겨주면 해당 데이터 형식에 해당하는 if 구문이 실행됩니다.

> void IsWhat(object o)
. {
.     if (o is int)
.         Console.WriteLine("Int");
.     else if (o is string)
.         Console.WriteLine("String");
.     else if (o is DateTime)
.         Console.Write("DateTime");
. }
> 
> IsWhat(1234)
Int
> IsWhat("Hello")
String
> IsWhat(DateTime.Now)
DateTime

15.1. is 연산자로 특정 형식인지 물어보기

코드: Is.cs

// is 연산자로 형식(타입) 비교하기 
using System;

class Is
{
    static void Main()
    {
        object x = 1234;

        if (x is int) //[1] is 연산자로 특정 형식인지 확인
        {
            Console.WriteLine($"{x}는 정수형으로 변환이 가능합니다.");
        }
    }
}
1234는 정수형으로 변환이 가능합니다.

[1]번 코드의 x is int 식을 통해서 x 변수의 값이 int 형식인지를 비교합니다. 만약, int 형식 또는 int 형식으로 변환이 가능하면 true를 반환해 줍니다.

15.2. is 연산자로 문자열 또는 정수 형식인지 확인하기

이번에는 is 연산자로 문자열 형식도 비교해보겠습니다.

코드: IsDemo.cs

> // is 연산자: 
> //     특정 형식인지 아닌지 비교
> //     특정 형식으로 변환이 가능하면 true, 그렇지 않으면 false 값을 반환
> object s = "안녕하세요.";
> object i = 1234;

> s is string
true

> if (s is string)
. {
.     Console.WriteLine(
.         $"[1] {s}는 null이 아니며 문자열 형식으로 변환이 가능합니다.");
. }
[1] 안녕하세요.는 null이 아니며 문자열 형식으로 변환이 가능합니다.

> i is int
true

> if (i is int)
. {
.     Console.WriteLine($"[2] {i}는 정수형으로 변환이 가능합니다.");
. }
[2] 1234는 정수형으로 변환이 가능합니다.

문자열을 담고 있는 변수인 ss is string 식에 의해 string으로 변환이 가능한지를 검사합니다. 검사가 통과되면 true를 반환합니다.

as 연산자로 형식 변환하기

as 연산자는 특정 데이터를 특정 데이터 형식으로 변환하는데 사용합니다. 주의할 것은 해당 데이터 형식이면 변환하고 그렇지 않으면 null을 반환합니다. 다음 샘플 코드를 보면 x1234as 연산자로 string으로 변환이 불가능하고 x"1234"string으로 변환이 가능함을 보여줍니다.

코드: ObjectToString.cs

> object x = 1234;
> string s = x as string;
> s
null
> object x = "1234";
> string s = x as string;
> s
"1234"

as 연산자로 object 형식을 string 형식으로 변환하기

프로젝트 기반 소스로 as 연산자를 사용해보겠습니다.

코드: As.cs

using System;

class As
{
    static void Main()
    {
        object x = 1234;         
        string s = x as string;
        Console.WriteLine(s == null ? "null" : s);
    }
}
null

변수 x1234와 같이 정수 형식의 데이터이기에 as 연산자로는 string으로 변환이 불가능하기에 null 값이 s 변수에 담깁니다.

as 연산자로 변환이 가능하면 변환하고 그렇지 않으면 null 반환하기

또 다른 as 연산자를 사용 예제를 다루어 보겠습니다.

코드: AsDemo.cs

// as 연산자: ~ 형식으로 변환이 가능하면 변환
using System;

class AsDemo
{
    static void Main()
    {
        object s = "반갑습니다.";
        string r1 = s as string;
        Console.WriteLine($"[1] {r1}");

        object i = 1234;
        string r2 = i as string;
        if (r2 == null)
        {
            Console.WriteLine("[2] null 입니다.");
        }

        object i2 = 3456;
        if (i2 is string)
        {
            string r3 = i2 as string;
            Console.WriteLine($"[3] {r3}");
        }
        else
        {
            Console.WriteLine("[3] 변환 불가.");
        }
    }
}
[1] 반갑습니다.
[2] null 입니다.
[3] 변환 불가.

Convert.ToString() 메서드와 같이 형식 변환이 가능하면 변환해주는 것과 달리 as 연산자는 반드시 지정한 데이터 형식이어야만 변환이 되고 그렇지 않으면 null이 저장되는 구조입니다.

패턴 매칭: if 문과 is 연산자 사용

C#은 if 문 또는 switch 문을 사용하는 패턴 매칭을 제공합니다. 다음 내용은 가볍게 살펴보고 넘어가도 됩니다. 앞서 살펴본 is 연산자를 if 문에서 사용시 패턴이 일치하면 새로운 변수를 선언하고 해당 값을 변수에 할당합니다. 다음 코드 샘플을 살펴보면 xstring으로 변환이 가능하면 s라는 string 형식의 변수를 선언하고 해당 값인 "1234"를 저장하고 해당 프로그램에서 사용할 수 있습니다. 특이한 점은 s 변수는 if 구문과 같은 범위에서 생성되는 변수입니다.

> object x = "1234";
> if (x is string s) { }
> s
"1234"

패턴 매칭을 if 구문과 is 연산자를 사용하여 구현하는 예제를 만들어 보겠습니다.

코드: PatternMatchingWithIf.cs

using System;
using static System.Console;

class PatternMatchingWithIf
{
    static void Main()
    {
        PrintStars(null);
        PrintStars("하나");
        PrintStars(5);
    }

    static void PrintStars(object o)
    {
        if (o is null)
        {
            return; // null 제외
        }

        if (o is string)
        {
            return; // 문자열 제외
        }

        // 패턴 매칭: 넘어온 값이 정수 형식이면 int number = o;
        if (!(o is int number))
        {
            return; // 정수형 이외의 값 제외
        }
        WriteLine(new String('*', number));
    }
}
*****

위 코드의 PrintStarts() 함수는 넘어온 값이 정수일 경우에만 해당 숫자만큼 '*' 기호를 반복해서 출력하는 기능을 제공합니다. null 또는 문자열 값이 넘어오면 아무런 기능을 하지 않고 정수 형식만 받아서 number 변수에 담아서 사용하는 형식으로 is 연산자를 사용하여 패턴 매칭을 구현할 수 있습니다.

추가로, 패턴 변수의 범위에 대한 내용은 다음 경로를 참고하세요.

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/pattern-matching.md#scope-of-pattern-variables

18. 장 요약

클래스는 C#의 필수 구성 요소입니다. 반드시 C# 프로그램에는 하나 이상의 클래스가 있어야 합니다. 이러한 클래스는 앞으로 배울 모든 클래스의 여러 멤버가 되는 구성 요소들을 묶어서 관리해주는 역할을 합니다. 우리가 지금까지 여러 번 사용했던 메서드(함수) 및 속성들을 비롯하여 클래스의 구성 요소들을 계속해서 학습해 나가겠습니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com