객체지향 - 정적(Static) 멤버
정적 멤버를 설명하기 위해서는 먼저 인스턴스 멤버를 이해해야 한다.
new 연산자를 통해 어떤 타입의 객체를 생성하면 그 객체는 인스턴스가 된다.
저번 포스팅에서의 예제를 가져와보자.
Car mycar = new Car("가솔린", "방탄 유리");
여기서 mycar는 new 연산자를 거쳤으므로 인스턴스가 된다.
인스턴스 멤버는, Car 클래스 내부에서 정의된 메서드와 필드를 가리킨다.
즉, 인스턴스 멤버는 인스턴스 필드와 인스턴스 메서드로 구성된다.
인스턴스 멤버가 new 연산자를 거쳐 메모리에 할당된 것들이라면, 정적 멤버는 new 연산자를 거치지 않을 것이다.
즉 new 연산자를 거치지 않고도 이미 메모리에 상주해 있는 멤버들을 가리켜 정적 멤버라고 하는 것이다.
이런 정적 멤버들은 어떤 상황에서 쓰일까?
학생들을 관리하는 프로그램을 만든다고 하자.
아래와 같이 Student 클래스를 만들고 학생이 추가될 때마다 생성자를 사용하면 될 것이다.
class Student
{
string name;
public Student(string name)
{
this.name = name;
}
}
그런데 생성자로 생성된 총 학생의 수를 구하고 싶으면 어떻게 해야 할까?
class Student
{
string name;
int CountOfStudents;
public Student(string name)
{
this.name = name;
CountOfStudents++;
}
}
CountOfStudents라는 인스턴스 필드를 하나 선언하고 생성자에 CountOfStudents++; 명령어를 추가했다.
이렇게 하면 CountOfStudents는 생성자 메서드가 실행될 때마다 숫자가 1씩 증가할 것이다.
그러나
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
using System;
namespace example
{
class Program
{
static void Main(string[] args)
{
Student student1 = new Student("A");
Student student2 = new Student("B");
Student student3 = new Student("C");
Console.WriteLine(?);
}
}
class Student
{
string name;
public int CountOfStudents;
public Student(string name)
{
this.name = name;
CountOfStudents++;
}
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
무엇을 출력해야 하는 것일까?
인스턴스가 3개 생성되었기 때문에 나는 CountOfStudents 가 3이길 기대한다.
하지만
Console.WriteLine(student3.CountOfStudents);
이렇게 출력해봐도 결과는 1이 나온다.
new 연산자를 사용해 인스턴스를 생성할 때마다 그 인스턴스는 새로운 메모리 영역에 할당된다는 것을 생각해본다면 이는 당연한 결과다.
이런 상황에서 정적 멤버가 필요한 것이다.
CountOfStudents를 정적 필드로 선언하기 위해 static 예약어를 추가해주자.
public static int CountOfStudents;
정적 멤버는 인스턴스의 생성과 관계가 없기 때문에 (인스턴스를 생성하지 않아도 이미 메모리가 할당되어 있다)
인스턴스.CountOfStudents 의 형태가 사용 불가하다.
정적 멤버를 쓰려면 그냥 클래스명.CountOfStudents 형태로 써주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
using System;
namespace example
{
class Program
{
static void Main(string[] args)
{
Student student1 = new Student("A");
Student student2 = new Student("B");
Student student3 = new Student("C");
Console.WriteLine(Student.CountOfStudents);
}
}
class Student
{
string name;
public static int CountOfStudents;
public Student(string name)
{
this.name = name;
CountOfStudents++;
}
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
실행하면 원하던 값 3이 출력된다.
정적 멤버에는 정적 필드뿐만 아니라 정적 메서드도 있다.
정적 메서드도 필드와 같은 맥락에서 사용해주면 된다.
다만 정적 메서드 안에서는 인스턴스 멤버로의 접근이 불가능하다.
정적 메서드는 new로 인스턴스를 생성하지 않아도 호출할 수 있는 메서드지만,
인스턴스 멤버는 반드시 new로 생성해야 하기 때문이다.
생성되지 않은 멤버를 어떻게 호출할 수 있겠는가?
정적 메서드가 있으므로 당연히 정적 생성자도 존재할 것이다.
정적 생성자는 정적 멤버를 초기화하는 기능을 하는 메서드다.
(따라서 형식 이니셜라이저(type initializer)라고 불리기도 한다)
또한 클래스 내에 단 한 개만 존재할 수 있으며 매개변수를 받을 수 없다.
c# 컴파일러는 정적 필드를 초기화하는 코드를 모두 정적 생성자로 옮긴다.
정적 생성자가 없다면 컴파일러가 알아서 만든다.
따라서 아래의 두 코드는 완전히 동일하다.
필드를 선언만 해도 0으로 초기화되는 이유이다.
1
2
3
4
5
6
7
8
9
10
11
12
|
class Student
{
string name;
public static int CountOfStudents;
public Student(string name)
{
this.name = name;
CountOfStudents++;
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Student
{
string name;
public static int CountOfStudents;
public Student(string name)
{
this.name = name;
CountOfStudents++;
}
static Student()
{
CountOfStudents = 0;
}
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
|
이는 정적 멤버에서뿐만 아니라 인스턴스 멤버들에서도 적용되는 규칙이다.
또한 정적 생성자에는 접근 제한자를 붙일 수 없다.
'C#' 카테고리의 다른 글
[C# / 객체지향] 6. 캡슐화와 정보 은닉 (0) | 2019.12.16 |
---|---|
[C# / 객체지향] 5. 네임스페이스(namespace) (0) | 2019.12.15 |
[C# / 객체지향] 3. 추상화와 클래스(Class) (0) | 2019.12.13 |
[C# / 객체지향] 2. 추상화는 무엇일까 (0) | 2019.12.12 |
[C#] FQDN 을 알아보자. (0) | 2019.12.11 |