개발꿈나무
[C# 교과서] 17. C# 활용(11) - 생성자, 소멸자 본문
생성자
생성자
- 생성자는 단어 그대로 개체를 생성하면서 무엇인가를 하고자 할 때 사용되는 메서드로 개체를 초기화(주로 클래스 내 필드를 초기화)하는 데 사용된다.
- 생성자는 생성자 이름이 클래스 이름과 동일하다는 규칙이 있다.
- 생성자는 매개변수가 없는 기본(default) 생성자가 있고, 매개변수를 원하는 만큼 정의해서 사용할 수 있는데 이 때 반환값은 가지지 않는다.
- 생성자도 static 생성자(정적 생성자)와 public 생성자(인스턴스 생성자)로 구분되며, 일반적으로 인스턴스 생성자를 많이 사용한다.
- 모든 클래스는 적어도 생성자 하나를 가지는데, 사용하지 않는 기본 생성자는 코드에서 생략 가능하다.
- 클래스 내에서 생성자를 자동으로 만들어주는 코드 조각(단축키)는 ctor을 입력한 수 Tab을 두 번 누르면 된다.
using System;
// 클래스
class ConstructorDemo
{
// 생성자
public ConstructorDemo()
{
Console.WriteLine("생성자가 호출되었습니다.");
}
// 진입점
static void Main()
{
ConstructorDemo c = new ConstructorDemo();
}
}
Car 클래스의 Car() 생성자는 Car 클래스의 인스턴스가 만들어질 때 자동으로 호출해서 실행된다.
using System;
namespace ClassAndInstance
{
//[1] Dog 클래스
public class Dog
{
//[2] name 필드
private string name;
//[3] name 매개변수를 받아서 name 필드에 저장하는 생성자
public Dog(string name)
{
this.name = name; // 넘겨온 name을 name 필드에 임시 저장
}
//[4] name 필드의 값을 출력하는 반환값이 있는 메서드
public string Cry()
{
return name + "이(가) 멍멍멍";
}
}
class Program
{
static void Main(string[] args)
{
//[?] 인스턴스/개체/객체는 실체, 구체적인 것 정의
//[5] Dog 클래스 사용: happy, worry => 인스턴스, 개체, ...
Dog happy = new Dog("해피");
Console.WriteLine(happy.Cry());
Dog worry = new Dog("워리");
Console.WriteLine(worry.Cry());
}
}
}
일반적으로 매개변수가 있는 생성자는 클래스 내에 선언된 특정 필드 값을 초기화하는 목적으로 사용된다.
생성자의 매개변수로 name을 선언했고 이 값을 사용하여 Dog 클래스의 name 필드를 초기화한다.
클래스에 생성자 여러 개 만들기
클래스에는 매개변수를 달리하여 생성자를 여러 개 만들 수 있는데 이러한 기능을 생성자 오버로드(constructor overload)라고 한다.
using System;
class ConstructorLog
{
public ConstructorLog()
{
Console.WriteLine("기본 생성자 실행");
}
public ConstructorLog(string message)
{
Console.WriteLine("오버로드된 생성자 실행 : " + message);
}
}
class ConstructorOverload
{
static void Main()
{
ConstructorLog log1 = new ConstructorLog();
ConstructorLog log2 = new ConstructorLog("C#");
ConstructorLog log3 = new ConstructorLog("ASP.NET");
}
}
정적 생성자와 인스턴스 생성자
클래스의 정적 멤버를 호출할 때 맨 먼저 호출되는 정적 생성자는 static 키워드로 만들며, 인스턴스 생성자는 public 키워드로 만든다.
using System;
namespace ConstructorAll
{
public class Person
{
private static readonly string _Name;
private int _Age;
//[1] 정적 생성자
static Person() { _Name = "홍길동"; }
//[2] 인스턴스 생성자: 매개변수가 없는 생성자
public Person() { _Age = 21; }
//[3] 인스턴스 생성자: 매개변수가 있는 생성자
public Person(int _Age)
{
this._Age = _Age; // this.필드 = 매개변수;
}
//[4] 정적 메서드
public static void Show()
{
Console.WriteLine("이름 : {0}", _Name);
}
//[5] 인스턴스 메서드
public void Print()
{
Console.WriteLine("나이 : {0}", _Age);
}
}
class ConstructorAll
{
static void Main()
{
//[A] 정적 생성자 실행
Person.Show(); // 정적인 멤버 호출
//[B] 인스턴스 생성자 실행
(new Person()).Print(); // 인스턴스 멤버 호출
(new Person(22)).Print();
}
}
}
/*
이름: 백승수
나이: 21
나이: 22
*/
[1] static이 붙는 생성자는 정적 생성자로 [A]처럼 정적인 멤버가 호출될 때 먼저 실행된다.
[2], [3] 인스턴스 생성자를 선언했고, [B]처럼 인스턴스 멤버가 호출될 때 실행된다.
this() 생성자로 또 다른 생성자 호출하기
생성자에서 this()는 자신의 또 다른 생성자를 의미하며 this() 생성자로 매개변수가 있는 생성자에서 매개변수가 없는 생성자를 호출하거나 또 다른 생성자들을 호출할 수 있다.
using System;
namespace ConstructorThis
{
class Say
{
private string message = "[1] 안녕하세요.";
public Say() => Console.WriteLine(this.message);
//[1] this() 생성자로 나 자신의 매개변수가 없는 생성자 먼저 호출
public Say(string message) : this()
{
//[2] 매개변수가 있는 생성자 자체도 호출
this.message = message;
Console.WriteLine(this.message);
}
}
class ConstructorThis
{
//[A] 매개변수가 있는 생성자 호출할 때 매개변수가 없는 생성자도 함께 호출
static void Main() => new Say("[2] 잘가요.");
}
}
/*
[1] 안녕하세요.
[2] 잘가요.
*/
[1] 매개면수가 있는 생성자 뒤에 콜론(:) 기호와 this()를 사용하여 자신의 매개변수가 없는 생성자를 먼저 호출하고 다시 매개변수가 있는 생성자를 호출한다.
생성자 포워딩
this() 생성자를 사용하면 생성자를 포워딩(forwarding)할 수 있으므로 다른 생성자에 값을 전달하기 좋다.
using System;
namespace ConstructorForwarding
{
class Money
{
public Money() : this(1000) { } // 아래 생성자로 전송
public Money(int money) => Console.WriteLine("Money: {0:#,###}", money);
}
class ConstructorForwarding
{
static void Main()
{
var basic = new Money(); // 1000
var bonus = new Money(2000); // 2000
}
}
}
생성자 뒤에 오는 this()는 자신의 도 다른 생성자를 의미하며,
이러한 형태로 다른 생성자를 사용하여 값을 전달할 수 있다.
생성자를 사용하여 읽기 전용 필드 초기화
필드를 정의할 때 readonly 키워드를 붙일 수 있는데 이렇게 만들어진 필드를 읽기 전용 필드라고 한다.
읽기 전용 필드는 클래스의 생성자로만 초기화가 가능하고, 생성자로 초기화한 후에는 값을 변경할 수 없다.
using System;
public class WhitchService
{
// 읽기 전용 필드
private readonly string _serviceName;
public WhitchService(string serviceName)
{
// 읽기 전용 필드는 생성자에 의해서 초기화해서 사용 가능
_serviceName = serviceName;
}
public void Run() => Console.WriteLine($"{_serviceName} 기능을 실행합니다.");
}
class ReadOnlyNote
{
static void Main()
{
var file = new WhitchService("[1] 파일 로그");
file.Run(); //[1] 파일 로그 기능을 실행합니다.
var db = new WhitchService("[2] DB 로그");
db.Run(); //[2] DB 로그 기능을 실행합니다.
}
}
읽기 전용은 선언과 동시에 초기화도 가능하고 선언한 후 생성자로 초기화할 수도 있다.
식 본문 생성자
화살표 연산자를 사용하여 함수를 줄여 표현하는 것처럼 생성자 코드를 줄여 표현할 수 있는데 이것을 식 본문 멤버 중이서 식 본문 생성자(expression bodied constructor)라고 한다.
using System;
namespace ExpressionBodiedConstructor
{
class Pet
{
private string _name;
// Expression Bodied Constructor
public Pet(string name) => _name = name;
public override string ToString()
{
return _name;
}
}
class ExpressionBodiedConstructor
{
static void Main()
{
var pet = new Pet("야옹이");
Console.WriteLine(pet.ToString());
}
}
}
소멸자
종료자
종료자(finalizer)라고도 하는 소멸자(destructor)는 닷넷의 가비지 수집기(Garbage Collector, GC)에서 클래스의 인스턴스를 사용한 후 최종 정리할 때 실행되는 클래스에서 가장 늦게 호출되는 메서드이다.
C#에서는 닷넷 가비지 수집기(GC)가 개체를 소멸할 때 메모리를 해제하는 등 역할을 대신해 주기 때문에 소멸자에 직접 접근할 일이 없다.
Class Car
{
~Car()
{
//개체가 소멸될 때 필요한 기능 수행
}
}
가비지 수집기
C#에서 메모리 관리는 닷넷에 내장된 GC라는 가비지 수집기가 관리한다.
특정 클래스의 인스턴스를 생성한 후 해당 인스턴스를 제거하는 코드를 따로 사용하지 않아도 되는 것이 GC 엔진이 하는 역할이다.
생성자, 메서드, 소멸자 실행 시점 살펴보기
using static System.Console;
public class DestructorTest
{
// 생성자
public DestructorTest()
{
WriteLine("[1] 생성");
}
// 메서드
public void Run()
{
WriteLine("[2] 실행");
}
// 소멸자: GC
~DestructorTest()
{
WriteLine("[3] 소멸");
}
}
class ConstructorToDestructor
{
static void Main()
{
DestructorTest test = new DestructorTest(); // 생성
test.Run(); // 실행
// GC.Collect(); // 소멸
}
}
/*
[1] 생성
[2] 실행
[3] 소멸
*/
소멸자를 사용한 클래스 역할 마무리하기
using System;
namespace DestructorDemo
{
public class Car
{
private string _name; // 필드
public string GetName() // 메서드
{
return _name;
}
public Car() // 생성자(매개 변수가 없는)
{
_name = "승용차";
}
public Car(string name) // 생성자(매개 변수가 있는)
{
this._name = name;
}
//[!] 소멸자 : GC가 호출
~Car() // 소멸자
{
Console.WriteLine("{0} 폐차...", _name);
}
}
class DestructorDemo
{
static void Main(string[] args)
{
Car car1 = new Car();
Console.WriteLine(car1.GetName());
Car car2 = new Car("캠핑카");
Console.WriteLine(car2.GetName());
}
}
}
/*
승용차
캠핑카
캠핑카 폐차...
승용차 폐차...
*/
가비지 수집기는 Stack 구조로 데이터를 처리하기 때문에 LIFO 형식으로 구현된다.
<Reference>
'C# 기초' 카테고리의 다른 글
[C# 교과서] 19. C# 활용(13) - 속성 사용하기 (0) | 2022.01.20 |
---|---|
[C# 교과서] 18. C# 활용(12) - 메서드와 매개변수 (0) | 2022.01.20 |
[C# 교과서] 16. C# 활용(10) - 네임스페이스, 필드 (0) | 2022.01.19 |
[C# 교과서] 15. C# 활용(9) - 개체 만들기 (0) | 2022.01.19 |
[C# 교과서] 14. C# 활용(8) - 알고리즘과 절차 지향 프로그래밍 (0) | 2022.01.19 |