public 필드의 유혹과 Get/Set메소드
필드는 public으로 선언하면 안된다
99프로의 필드는 private
필드의 값을 접근 및 변경할때 필드를 public으로 선언해놓고 Get/Set메소드 대신,
외부에서 cat.height = 100; 을 사용하는 것 처럼 " = " 연산자로 필드를 읽거나 할당하고 싶어진다
이렇게 되면 외부에서 클래스 내부의 필드에 쉽게 접근할 수 있기 때문에
객체 지향의 은닉성이 위반되고 자칫 데이터가 오염되기 쉽다
그렇기 때문에 통상적으로
은닉성을 지키고 접근 및 변경을
프로그래머가 원하는 대로 (조건문을 추가하거나 등) 할 수 있게 하기 위해
Get/Set메소드를 일일이 구현해주는 것이고,
한단계 발전하여 이를 간단하게 해주는게 프로퍼티다
Get/Set메소드?
Get/Set이라는 키워드나 라이브러리가 있는것이 아니라, 한마디로 Get은 데이터를 외부에 출력,
Set은 내부에 데이터를 입력하는 프로그래머가 만든 원시적인 메소드이다
예를 들어 다음과 같은 클래스가 있다고 해보자
class MyClass
{
private int myField;
}
우리가 알고 있는 지식 선에서는 아마 다음과 같이 GetXXX()와 SetXXX() 메소드를 클래스에 추가해서 MyField에 접근할 수 있도록 할 것이다 ( 외부에서도 private/protected 속성에 접근하게 해주기 위해)
class MyClass
{
private int myFiled;
public int GetMyField(){return myField; }
public void SetMyField(int NewValue) {myField = NewValue;}
//pivate으로 선언된 클래스 내부필드를
//public으로 선언된 이 메소드를 사용해 외부에서 접근 및 변경가능
- 외부에 데이터를 출력할 때 = get 접근자
- 내부에 데이터를 입력할 때 = set접근자
그리고 이 클래스의 객체는 다음과 같이 사용할 것이다
MyClass obj = new MyClass();
obj.SetMyField( 3 );
Console.WriteLine(obj.GetMyFiled());
만약 public 으로 선언된 필드를 변경하고 싶다면 cat.height = 100;처럼
height값은 100이든 10000이든 가능할 것이라 오류 발생 가능성이 매우높지만
위와 같이 set 메소드를 정의하면
여기에 조건문을 추가하던지 해서 온도100 이상을 받으면 100으로만 유지하게 하는 등
접근 단계부터 제어가 가능하다
하지만 프로젝트가 커지고 필드에 접근할 일이 점점 늘어나면
이 Get, Set메소드가 무수히 늘어날 것이다
그래서 C#에는 겟셋메소드를 간략히 해주는 프로퍼티가 있다
프로퍼티
- 위에서 사용했던 원시적인 Get/Set메소드를 간편하게 해주는 것
- public필드를 다루듯, Get/Set메소드를 이용하여 내부 필드에 접근하게 해주는 멤버
(그렇지만 public 필드와는 다르게 public필드는 오염가능성이 크지만, 프로퍼티는 get/set을 따로 구현함으로써 내부 필드를 안전하게 보호해줌)
프로퍼티는 다음과 같이 선언한다
class 클래스이름
{
데이터형식 필드이름; //이 데이터 형식과
접근한정자 데이터형식 프로퍼티이름 //여기의 데이터형식은 같아야 함
{
get
{
return 필드이름;
}
set
{
필드이름 = value; //value 는 set접근자의 암묵적 매개변수로 간주
}
}
}
프로퍼티 선언 문법에서 get{ ...)과 set{...}을 일컬어 접근자라고 한다
get접근자는 필드로부터 값을 읽어오고 (+외부에 데이터를 출력할때 호출)
set접근자는 필드에 값을 할당한다(+ 내부에 데이터를 입력)
set접근자 안의 value를 주목해보자
value를 누구도 선언한 적 없지만, C#컴파일러는 set접근자의 암묵적 매개변수로 간주하므로 전혀 문제 삼지 않는다
아까 작성한 MyClass의 GetMyField() 메소드와 SetMyField()메소드를 프로퍼티로 바꿔보자
class MyClass
{
private int myField;
public int MyField //MyFiled는 프로퍼티이름
{
get
{
return myField;
}
set
{
myField = value;
}
}
}
이제 MyClass의 객체는 " = " 할당 연산자를 통해 myField 필드에 데이터를 저장하고 또 반대로 읽어올 수도 있다
MyClass obj = new MyClass();
obj.MyField = 3; //MyField는 프로퍼티이름
//이 코드로 get, set접근자가 적용되어 데이터 저장, 읽기 가능
Console.WriteLine(obj.MyFiled);
obj.MyFiled = 3; 을 해줌으로써 get, set접근자가 적용되어 데이터 저장, 읽기가 가능해졌다
이것이 프로퍼티의 장점이다 마치 프로퍼티를 public 필드 다루듯이 해주니까
(위에서 잘못된 public필드사용의 예시로 든 cat.height = 3; 와 비슷하게 다루게 해준다!)
프로퍼티는 데이터의 오염에 대해선 메소드처럼 안전하고, 데이터를 다룰 때는 필드처럼 간결하다
Get Set 메소드를 통해 필드가 변경되지 않기를 원할 때는 Set()메소드를 구현하지 않으면 됐다
그럼 프로퍼티를 통해서 필드가 변경되지 않도록 하려면? (= 읽기 전용)
: 마찬가지로 set 접근자를 구현하지 않으면 된다
하지만 쓰기 전용 프로퍼티로 get을 생략하는 것은 문법적으로 아무 문제가 없지만 신중하게 생각.
만든 클래스를 사용할 다른 프로그래머들에게 쓰기 전용 프로퍼티의 용도와 동작 결과를
확인 할 수 있는 방법을 알릴 수 있어야 한다
using System;
using static System.Console;
namespace Sample
{
class BirthdayInfo
{
private string name;
private DateTime birthday;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public DateTime BirthDay
{
get
{
return birthday;
}
set
{
birthday = value;
// Console.WriteLine(value.GetType());
}
}
public int Age
{
get
{
return new DateTime(DateTime.Now.Subtract(birthday).Ticks).Year;
}
}
}
class MainClass
{
public static void Main(string[] args)
{
BirthdayInfo birth = new BirthdayInfo();
birth.Name = "지훈"; //얘가 set접근자의 암묵적 매개변수 value로 들어감
birth.BirthDay = new DateTime(1999, 09, 22);
Console.WriteLine($"{birth.Name}");
WriteLine($"{birth.BirthDay}");
WriteLine($"{birth.Age}");
}
}
}
지웅이의 부가 설명
일단 프로퍼티를 구현하는 목적이 외부에서 접근이 가능한 값 = 전부 프로퍼티로 설정 이 베이스
좀 직관적으로 public int quiestion필드를 만들고 싶을때
public int quiestion; 이라는 필드를 써서 외부에서 접근가능하되 안정성을 해치는 public필드를 만드는 대신
프로퍼티로 public string Question{get; set;}을 해주는 것이다
그리고 외부에서 접근가능한 값을 만들고 싶다면 나중을 위해서라도 프로퍼티로 구현하는게 바람직하다
(위에 써놓았듯이 필드는 절대로 public x)
만약
public string question; 이라고 해놓고
다른 몇백개의 메서드나 클래스에서
People.name = " 지훈 " ; 이런식으로 썼다고 쳐보자
근데 갑자기 question에 접근할때 사람의 이름이 바뀌면 게임상에서 표시되게하는 UpdateText();
라는 메소드를 호출해줘야 된다면 (유니티에선 NameText.text = Name; 이런식으로 구현하겠지)
근데
People.name = " 지훈 "
People.name = " 지훈2"
식으로 사용했던걸 일일이 고쳐줘야 한다
하지만 프로퍼티를 사용했다면 Set에 UpdateText();만 추가해주면 다 바뀐다
즉 외부에서 접근이 가능하게 하려는 값을 만들땐 나중을 위해서라도 미리미리 프로퍼티로 구현하라는게 이런 의미다
참고로 이 때
1. 유니티 Update()에서 매 프레임마다 NameText.txt를 갱신하기
2. 이름이 바뀔 때만 갱신하기
어떤게 베스트일까? 당연히 2번 1번은 쩌리코더들이 한다
그럼 Name이 바뀔대마다 text를 갱신하는게 가장 좋은데
이게 이름이 바뀌는걸 어떻게 알까
Update마다 전에 있던 이름을 저장해놓고 현재이름이 바뀌면 체크해주는 정도가 베스트인데
퍼포먼스(?)적으로 많이 손해이기 때문에
public void SetName(string name)
{
this.name = name;
UpdateText();
}
프로퍼티가 없으면 원시적으로 이렇게 구현할 할 거다
이러면 메소드 사용해서 name값에 접근할 때마다 UpdateText();가 호출되면서 표시되게 할 수 있다
근데, C#은 프로퍼티를 지원하니까 편하게
public void SetName
{
get
{
return name
};
set
{
name = value;
UpdateText();
}
}
public void Question {get; } :
읽기 전용. 생성자로 초기화한(=변경) 뒤로 아예 변경 불가능
( get;만 써서 읽기전용으로 만들었다고 해서 아예 값 지정이 안되는게 아니라
생성자에선 접근이 가능하다 )
public void Question {get; private set;} :
: 외부에서 "접근" 은 가능하게 하되 "변경"은 클래스 내부에서만 가능하도록
public void Question {get; init set;} :
생성자로 초기화한 이후에도 처음 1회 set 가능
public 필드의 유혹과 Get/Set메소드
필드는 public으로 선언하면 안된다
99프로의 필드는 private
필드의 값을 접근 및 변경할때 필드를 public으로 선언해놓고 Get/Set메소드 대신,
외부에서 cat.height = 100; 을 사용하는 것 처럼 " = " 연산자로 필드를 읽거나 할당하고 싶어진다
이렇게 되면 외부에서 클래스 내부의 필드에 쉽게 접근할 수 있기 때문에
객체 지향의 은닉성이 위반되고 자칫 데이터가 오염되기 쉽다
그렇기 때문에 통상적으로
은닉성을 지키고 접근 및 변경을
프로그래머가 원하는 대로 (조건문을 추가하거나 등) 할 수 있게 하기 위해
Get/Set메소드를 일일이 구현해주는 것이고,
한단계 발전하여 이를 간단하게 해주는게 프로퍼티다
Get/Set메소드?
Get/Set이라는 키워드나 라이브러리가 있는것이 아니라, 한마디로 Get은 데이터를 외부에 출력,
Set은 내부에 데이터를 입력하는 프로그래머가 만든 원시적인 메소드이다
예를 들어 다음과 같은 클래스가 있다고 해보자
class MyClass
{
private int myField;
}
우리가 알고 있는 지식 선에서는 아마 다음과 같이 GetXXX()와 SetXXX() 메소드를 클래스에 추가해서 MyField에 접근할 수 있도록 할 것이다 ( 외부에서도 private/protected 속성에 접근하게 해주기 위해)
class MyClass
{
private int myFiled;
public int GetMyField(){return myField; }
public void SetMyField(int NewValue) {myField = NewValue;}
//pivate으로 선언된 클래스 내부필드를
//public으로 선언된 이 메소드를 사용해 외부에서 접근 및 변경가능
- 외부에 데이터를 출력할 때 = get 접근자
- 내부에 데이터를 입력할 때 = set접근자
그리고 이 클래스의 객체는 다음과 같이 사용할 것이다
MyClass obj = new MyClass();
obj.SetMyField( 3 );
Console.WriteLine(obj.GetMyFiled());
만약 public 으로 선언된 필드를 변경하고 싶다면 cat.height = 100;처럼
height값은 100이든 10000이든 가능할 것이라 오류 발생 가능성이 매우높지만
위와 같이 set 메소드를 정의하면
여기에 조건문을 추가하던지 해서 온도100 이상을 받으면 100으로만 유지하게 하는 등
접근 단계부터 제어가 가능하다
하지만 프로젝트가 커지고 필드에 접근할 일이 점점 늘어나면
이 Get, Set메소드가 무수히 늘어날 것이다
그래서 C#에는 겟셋메소드를 간략히 해주는 프로퍼티가 있다
프로퍼티
- 위에서 사용했던 원시적인 Get/Set메소드를 간편하게 해주는 것
- public필드를 다루듯, Get/Set메소드를 이용하여 내부 필드에 접근하게 해주는 멤버
(그렇지만 public 필드와는 다르게 public필드는 오염가능성이 크지만, 프로퍼티는 get/set을 따로 구현함으로써 내부 필드를 안전하게 보호해줌)
프로퍼티는 다음과 같이 선언한다
class 클래스이름
{
데이터형식 필드이름; //이 데이터 형식과
접근한정자 데이터형식 프로퍼티이름 //여기의 데이터형식은 같아야 함
{
get
{
return 필드이름;
}
set
{
필드이름 = value; //value 는 set접근자의 암묵적 매개변수로 간주
}
}
}
프로퍼티 선언 문법에서 get{ ...)과 set{...}을 일컬어 접근자라고 한다
get접근자는 필드로부터 값을 읽어오고 (+외부에 데이터를 출력할때 호출)
set접근자는 필드에 값을 할당한다(+ 내부에 데이터를 입력)
set접근자 안의 value를 주목해보자
value를 누구도 선언한 적 없지만, C#컴파일러는 set접근자의 암묵적 매개변수로 간주하므로 전혀 문제 삼지 않는다
아까 작성한 MyClass의 GetMyField() 메소드와 SetMyField()메소드를 프로퍼티로 바꿔보자
class MyClass
{
private int myField;
public int MyField //MyFiled는 프로퍼티이름
{
get
{
return myField;
}
set
{
myField = value;
}
}
}
이제 MyClass의 객체는 " = " 할당 연산자를 통해 myField 필드에 데이터를 저장하고 또 반대로 읽어올 수도 있다
MyClass obj = new MyClass();
obj.MyField = 3; //MyField는 프로퍼티이름
//이 코드로 get, set접근자가 적용되어 데이터 저장, 읽기 가능
Console.WriteLine(obj.MyFiled);
obj.MyFiled = 3; 을 해줌으로써 get, set접근자가 적용되어 데이터 저장, 읽기가 가능해졌다
이것이 프로퍼티의 장점이다 마치 프로퍼티를 public 필드 다루듯이 해주니까
(위에서 잘못된 public필드사용의 예시로 든 cat.height = 3; 와 비슷하게 다루게 해준다!)
프로퍼티는 데이터의 오염에 대해선 메소드처럼 안전하고, 데이터를 다룰 때는 필드처럼 간결하다
Get Set 메소드를 통해 필드가 변경되지 않기를 원할 때는 Set()메소드를 구현하지 않으면 됐다
그럼 프로퍼티를 통해서 필드가 변경되지 않도록 하려면? (= 읽기 전용)
: 마찬가지로 set 접근자를 구현하지 않으면 된다
하지만 쓰기 전용 프로퍼티로 get을 생략하는 것은 문법적으로 아무 문제가 없지만 신중하게 생각.
만든 클래스를 사용할 다른 프로그래머들에게 쓰기 전용 프로퍼티의 용도와 동작 결과를
확인 할 수 있는 방법을 알릴 수 있어야 한다
using System;
using static System.Console;
namespace Sample
{
class BirthdayInfo
{
private string name;
private DateTime birthday;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public DateTime BirthDay
{
get
{
return birthday;
}
set
{
birthday = value;
// Console.WriteLine(value.GetType());
}
}
public int Age
{
get
{
return new DateTime(DateTime.Now.Subtract(birthday).Ticks).Year;
}
}
}
class MainClass
{
public static void Main(string[] args)
{
BirthdayInfo birth = new BirthdayInfo();
birth.Name = "지훈"; //얘가 set접근자의 암묵적 매개변수 value로 들어감
birth.BirthDay = new DateTime(1999, 09, 22);
Console.WriteLine($"{birth.Name}");
WriteLine($"{birth.BirthDay}");
WriteLine($"{birth.Age}");
}
}
}
지웅이의 부가 설명
일단 프로퍼티를 구현하는 목적이 외부에서 접근이 가능한 값 = 전부 프로퍼티로 설정 이 베이스
좀 직관적으로 public int quiestion필드를 만들고 싶을때
public int quiestion; 이라는 필드를 써서 외부에서 접근가능하되 안정성을 해치는 public필드를 만드는 대신
프로퍼티로 public string Question{get; set;}을 해주는 것이다
그리고 외부에서 접근가능한 값을 만들고 싶다면 나중을 위해서라도 프로퍼티로 구현하는게 바람직하다
(위에 써놓았듯이 필드는 절대로 public x)
만약
public string question; 이라고 해놓고
다른 몇백개의 메서드나 클래스에서
People.name = " 지훈 " ; 이런식으로 썼다고 쳐보자
근데 갑자기 question에 접근할때 사람의 이름이 바뀌면 게임상에서 표시되게하는 UpdateText();
라는 메소드를 호출해줘야 된다면 (유니티에선 NameText.text = Name; 이런식으로 구현하겠지)
근데
People.name = " 지훈 "
People.name = " 지훈2"
식으로 사용했던걸 일일이 고쳐줘야 한다
하지만 프로퍼티를 사용했다면 Set에 UpdateText();만 추가해주면 다 바뀐다
즉 외부에서 접근이 가능하게 하려는 값을 만들땐 나중을 위해서라도 미리미리 프로퍼티로 구현하라는게 이런 의미다
참고로 이 때
1. 유니티 Update()에서 매 프레임마다 NameText.txt를 갱신하기
2. 이름이 바뀔 때만 갱신하기
어떤게 베스트일까? 당연히 2번 1번은 쩌리코더들이 한다
그럼 Name이 바뀔대마다 text를 갱신하는게 가장 좋은데
이게 이름이 바뀌는걸 어떻게 알까
Update마다 전에 있던 이름을 저장해놓고 현재이름이 바뀌면 체크해주는 정도가 베스트인데
퍼포먼스(?)적으로 많이 손해이기 때문에
public void SetName(string name)
{
this.name = name;
UpdateText();
}
프로퍼티가 없으면 원시적으로 이렇게 구현할 할 거다
이러면 메소드 사용해서 name값에 접근할 때마다 UpdateText();가 호출되면서 표시되게 할 수 있다
근데, C#은 프로퍼티를 지원하니까 편하게
public void SetName
{
get
{
return name
};
set
{
name = value;
UpdateText();
}
}
public void Question {get; } :
읽기 전용. 생성자로 초기화한(=변경) 뒤로 아예 변경 불가능
( get;만 써서 읽기전용으로 만들었다고 해서 아예 값 지정이 안되는게 아니라
생성자에선 접근이 가능하다 )
public void Question {get; private set;} :
: 외부에서 "접근" 은 가능하게 하되 "변경"은 클래스 내부에서만 가능하도록
public void Question {get; init set;} :
생성자로 초기화한 이후에도 처음 1회 set 가능