3. Field
'자동차'는 자신만의 차량번호와 생산이 완료되 시장에 나오는 출고일이라는 날짜가 존재할 수 있습니다. 이 값을 다루기 위해 이전에 만든 Car클래스에서는 Field를 아래와 같이 추가합니다.
public class Car
{
public string Number;
public DateTime FDate;
}
예제에서는 Field에서 string과 DateTime형식을 사용했지만 Field를 만드는 데는 int나 Array 등 C#에서 다룰 수 있는 모든 형식뿐만 아니라 Car와 같은 자신이 직접 만든 형식까지도 사용할 수 있습니다.
● 접근 한정자
'접근 한정자'는 캡슐화 과정에서 Field를 어떻게 외부에 노출할 것인지를 지정하는 것입니다. 위 예제에서 Car클래스의 Number과 Color라는 Field에는 접근 한정자로 'public'이 지정되었습니다. 사실 접근 한정자를 아무것도 지정하지 않을 수도 있는데 이럴 경우 기본적으로 'private'한정자가 기본으로 적용되며 해당 Field에는 오로지 클래스 내부에서만 접근할 수 있는 상태가 됩니다.
접근 한정자로 사용할 수 있는 keyword는 아래와 같이 4가지정도가 있으며 서로 다른 keyword가 결합되어 해당 keyword별 특징을 그대로 적용하여 사용될 수 도 있습니다.
private | 아무것도 지정하지 않을 경우 기본이 되며 클래스와 같은 Type의 내부에서만 접근이 가능합니다. |
internal | private과 같지만 같은 Assembly안에서도 접근할 수 있습니다. |
protected | private과 같지만 해당 Type을 상속하는 자식 Type에서도 접근할 수 있습니다. |
public | Type의 외부에서 자유롭게 접근할 수 있습니다. |
internal protected | internal과 protected를 동시에 적용합니다. |
private protected | private과 protected를 동시에 적용합니다. 이 한정자는 C# 7.2부터 사용가능합니다. |
● Field에서 값 다루기
Car와 같은 Type이 만들어 지고 해당 Type의 인스턴스가 생성되었다면 아래와 같이 원하는 필드에 값을 설정하고 해당 값을 확인할 수 있습니다.
using mylibrary;
Car c = new();
c.Number = "1234";
c.FDate = new DateTime(2022, 5, 15);
Console.WriteLine($"차량번호 {c.Number}번은 {c.FDate:yyyy년 MM월 dd일}에 출고되었습니다.");
//차량번호 1234번은 2022년 05월 15일에 출고되었습니다.
혹은 다음과 같이 객체의 인스턴스를 생성함과 동시에 Field에 값을 초기화할 수도 있습니다.
Car c = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15)
};
● enum 사용하기
Field에 값을 저장하는 경우 상황에 따라서는 제한된 값만을 설정하도록 해야하는 경우가 있습니다. 이런 경우 enum(열거형)을 사용할 수 있는데 예를 들어 선택 가능한 차량의 색상을 표현하고자 하는 경우 아래와 같이 enum을 정의하고
public enum CarColor
{
Red,
Blue,
Yellow,
White,
Black
}
해당 Type에 정의한 enum형식의 Field를 추가하여
namespace mylibrary;
public class Car
{
public string Number;
public DateTime FDate;
public CarColor MyColor;
}
위에서 열거된 색상만을 선택할 수 있도록 하는 것입니다.
using mylibrary;
Car c = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15),
MyColor = CarColor.Black
};
Console.WriteLine($"차량의 색상은 {c.MyColor}입니다.");
//차량의 색상은 Black입니다.
enum은 내부적인 값을 int형식으로 다루게 되며 0부터 순서대로 값을 할당합니다. 따라서 예제에서의 enum값중 Red는 0을 Yellow는 2의 값을 가지게 됩니다. 물론 필요하다면 이 규칙을 깨고 임의의 정수 값을 개별적으로 할당할 수 있습니다.
public enum CarColor
{
Red,
Blue,
Yellow = 3,
White,
Black
}
예를 들어 Yellow에 3을 할당했다면 Yellow는 3의 값을 가지게 되며 White는 4, Black는 5의 순서로 값이 할당됩니다.
enum은 단일값이 아닌 여러 값을 결합하여 할당할 수도 있는데 이러한 동작은 enum에 [System.Flags] 어트리뷰트를 적용함으로써 가능합니다.
[System.Flags]
public enum CarColor
{
Red = 1,
Blue = 2,
Yellow = 4,
White = 8,
Black = 16
}
예제에서는 각 요소의 값을 1, 2, 4, 8, 16으로 구분하여 할당하였습니다. 이는 여러개의 값이 지정되는 할당된 값이 겹치는 경우를 피하기 위해서입니다. 예를 들어 기존처럼 순서대로 값이 할당될 때 Red는 0이 되고 Blue는 1이 되는데 Red와 Blue가 동시에 할당되는 경우 0+1은 1이 되므로 값은 Blue로 결정되는 문제가 발생할 수 있습니다.
혹은 숫자대신 bit의 각 자릿수로 구분할 수 있도록 다음과 같은 방법으로도 요소에 값을 할당할 수 있습니다.
[System.Flags]
public enum CarColor
{
Red = 0b_0000_0001,
Blue = 0b_0000_0010,
Yellow = 0b_0000_0100,
White = 0b_0000_1000,
Black = 0b_0001_0000
}
또한 enum은 기본적으로 int형식으로 유지되지만 필요하다면 정수형의 다른 형식을 지정할 수도 있습니다.
[System.Flags]
public enum CarColor : byte
{
Red = 0b_0000_0001,
Blue = 0b_0000_0010,
Yellow = 0b_0000_0100,
White = 0b_0000_1000,
Black = 0b_0001_0000
}
고의적으로 높은 값을 설정하지 않는 이상 굳이 int형을 유지할 필요가 없으므로 메모리 절약을 위해 낮은 형식을 지정하여 사용하는 것입니다.
이 상태에서 여러 개의 enum값을 할당하는 경우에는 |로 구분하여 할당해야 합니다.
Car c = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15),
MyColor = CarColor.Red | CarColor.Blue
};
Console.WriteLine($"차량의 색상은 {c.MyColor}입니다.");
//차량의 색상은 Red, Blue입니다.
그리고 할당된 값은 , 로 분리된 문자열 값으로서 확인할 수 있습니다.
● Collection 사용하기
고속도로를 달리다 보면 뒤에 차량을 여러 대 싣고 운반하는 트럭을 볼 수 있습니다. 이런 상황을 가정해 Car에 다음과 같은 Field를 추가하였습니다.
public class Car
{
public string Number;
public DateTime FDate;
public CarColor MyColor;
public List<Car> Cars = new();
}
예제에서 사용된 generic List<T>를 사용하면 지정한 모든 형식을 정렬된 상태로 저장하고 다룰 수 있습니다. 또한 new()를 통해 collection을 List<Car>라는 형식으로 초기화하고 있는데 이는 현재 Cars는 null이므로 Add()와 같은 메서드를 통해 Item을 추가할 수 있도록 해야 하기 때문입니다.
List<Car>에서 <Car>는 'generics'이라고 하는 것으로 Collection을 강력한 형식(strongly typed)으로 만들기 위한 것입니다. Object를 사용하는 것과는 달리 이러한 방법은 코드의 성능과 정확성을 향상시 키 킬 수 있습니다.
위와 같이 생성된 Cars Field는 아래와 같이 Add() 메서드를 통해 요소를 추가할 수 있습니다.
Car c = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15),
MyColor = CarColor.Red | CarColor.Blue
};
c.Cars.Add(new Car() { FDate = new DateTime(2022, 1, 1), MyColor = CarColor.Black });
c.Cars.Add(new Car() { FDate = new DateTime(2022, 2, 1), MyColor = CarColor.White });
foreach(Car item in c.Cars)
{
Console.WriteLine(item.MyColor);
}
//Black
//White
● static
이전에 만들어진 Car Library를 사용해 instance를 생성하면 각각의 instance에 해당하는 Field는 생성된 instance마다 서로 다른 값을 가질 수 있습니다.
using mylibrary;
Car sedan = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15)
};
Car truck = new()
{
Number = "5678",
FDate = new DateTime(2022, 3, 10)
};
Console.WriteLine(sedan.Number);
Console.WriteLine(truck.Number);
//1234
//5678
그러나 클래스에서 Field를 static으로 정의하게 되면 해당 Field의 값은 모든 instance에서 공유할 수 있게 됩니다.
예를 들어서 위 예제의 경우 차량의 제조사를 명시하기 위한 필드를 만들게 된 경우
namespace mylibrary;
public class Car
{
public string? Number;
public DateTime FDate;
public static string? brand;
}
클래스를 통해 직접 값을 할당하게 되면 해당 값은 각 instance에서 가져와 공유할 수 있게 됩니다.
using mylibrary;
Car sedan = new()
{
Number = "1234",
FDate = new DateTime(2022, 5, 15)
};
Car truck = new()
{
Number = "5678",
FDate = new DateTime(2022, 3, 10)
};
Car.brand = "잘나가 자동차";
Console.WriteLine($"{Car.brand}의 {sedan.Number}번 자동차");
Console.WriteLine($"{Car.brand}의 {truck.Number}번 자동차");
//잘나가 자동차의 1234번 자동차
//잘나가 자동차의 5678번 자동차
위와 같이 static으로 정의된 것을 static member라고 합니다. member라는 말에서 알 수 있듯이 static은 Field뿐만 아니라 Method나 생성자(Constructor), Property 등 다양한 곳에서 적용될 수 있습니다.
● const
값이 바뀌지 않는 경우라면 Field에 const를 키워드를 붙여줄 수 있습니다.
namespace mylibrary;
public class Car
{
public string? Number;
public DateTime FDate;
public const string brand = "잘나가 자동차";
}
const는 위 예제에서 처럼 선언과 동시에 값이 할당되며 이후부터는 할당된 값은 변경할 수 없습니다. 이는 컴파일 단계에서 const Field 자체가 할당된 값으로 바뀌기 때문입니다.
마이크로 소프트에서도 const를 직접 활용하고 있는데 예를 들어 int형 변수의 최댓값을 확인하는 System.Int32.MaxValue에서도 MaxValue를 const Field를 정의한 것을 확인할 수 있습니다.
● readonly
값이 불변하는 또 다른 방법으로 readonly를 사용할 수 있습니다.
namespace mylibrary;
public class Car
{
public string? Number;
public DateTime FDate;
public readonly string brand = "잘나가 자동차";
}
readonly가 const와 다른 점은 사전에 미리 값을 할당할 필요가 없기 때문에 생성자나 매개변수 할당을 통해서
namespace mylibrary;
public class Car
{
public Car(string _brand)
{
brand = _brand;
}
public string? Number;
public DateTime FDate;
public readonly string? brand;
}
필요한 초기값을 할당할 수 있다는 것입니다.
Car sedan = new(_brand: "잘나가 자동차")
{
Number = "1234",
FDate = new DateTime(2022, 5, 15)
};
참고로 모든 인스턴스에서 값을 공유하기 위해 readonly는 static으로도 선언될 수 있습니다.
● 생성자 초기화
필드는 Type이 정의될 때가 아닌 runtime에서 초기화가 필요할 수도 있습니다. 이런 경우 코드상에서 생성자를 통해 각 필드의 초기화를 진행할 수 있습니다. 생성자 초기화는 이미 readonly를 사용할 때 잠깐 언급했었는데 다음과 같이 할 수 있는 것입니다.
namespace mylibrary;
public class Car
{
public Car()
{
FDate = DateTime.Now;
}
public string? Number;
public DateTime FDate;
}
위 예제는 생성자를 통해 FDate값을 초기화하고 있습니다. 초기화 시간은 생성자가 실행되는 순간의 시간입니다.
using mylibrary;
Car sedan = new()
{
Number = "1234"
};
Car truck = new()
{
Number = "5678"
};
Console.WriteLine($"{sedan.Number}번 자동차의 출고일:{sedan.FDate}");
Console.WriteLine($"{truck.Number}번 자동차의 출고일:{truck.FDate}");
//1234번 자동차의 출고일:2022-05-02 오전 10:42:53
//5678번 자동차의 출고일:2022-05-02 오전 10:42:53
생상자는 객체를 new키워드를 통해 인스턴스화 할 때 실행되는 특수한 메서드로서 필요한 경우 각각의 여러 옵션을 가진 생성자를 다수 만들어두고 사용할 수도 있습니다.
public Car()
{
FDate = DateTime.Now;
}
public Car(DateTime _FDate)
{
FDate = _FDate;
}
예제에서 Car클래스는 DateTime형식의 값을 받는 두 번째 생성자를 추가하였습니다. 이에 따라 Car의 인스턴스를 생성할 때는 이전처럼 아무런 인수값이 없거나 필요한 인수값을 선택적으로 전달할 수 있습니다.
using mylibrary;
Car sedan = new(new DateTime(2021, 3, 4))
{
Number = "1234"
};
Car truck = new()
{
Number = "5678"
};
Console.WriteLine($"{sedan.Number}번 자동차의 출고일:{sedan.FDate}");
Console.WriteLine($"{truck.Number}번 자동차의 출고일:{truck.FDate}");
//1234번 자동차의 출고일:2021-03-04 오전 12:00:00
//5678번 자동차의 출고일:2022-04-03 오전 10:52:45
'.NET > C#' 카테고리의 다른 글
[C#] C#과 OOP(Object-Oriented Programming) - 5. 속성(Property)과 인덱서(Indexer) (0) | 2022.06.24 |
---|---|
[C#] C#과 OOP(Object-Oriented Programming) - 4. Method (메서드) (0) | 2022.06.24 |
[C#] C#과 OOP(Object-Oriented Programming) - 2. 클래스 라이브러리 (Class library) (0) | 2022.06.24 |
[C#] C#과 OOP(Object-Oriented Programming) - 1. 객체지향프로그래밍 개념 (0) | 2022.06.24 |
[C#] 예외처리 (0) | 2022.06.24 |