타입 캐스팅
int a = 100;
short b = (short)a;
float c = a;
int d = (int)c;
변수를 저장하는 큰 상자를 작은 상자에 넣기위해 캐스팅을 한다.
큰상자에 작은 상자를 넣는 것은 상관없지만, 작은 상자에 큰 상자를 넣는 것은 안된다.
문자열 입출력
//string >> int
string input = Console.ReadLine();
int number = int.Parse(input);
Console.WriteLine(number);
Console.ReadLIne으로 입력받은 값은 string값으로 출력된다.
여기에서 console.readline은 콘솔창에 엔터를 누를 때 까지 기다린다는 의미이다.
만약 숫자를 입력 후 숫자로 저장받고 싶다면 int.Parse()를 사용한다.
Convert.Toint32(Console.ReadLIne()); 로도 정수 변환이 가능하다.
둘 다 문자열을 정수로 변환하지만, int.Parse()는 반드시 string을 입력받고, Convert.Toint32()는 다양한 형식을 입력받을 수 있다.
//int >> string
int a = 1;
int b = 2;
string message = string.Format("a는 {0} b는 {1}", a, b);
Console.WriteLine(message);
// 또는
string message = $"a는 {a}, b는 {b}";
Console.WriteLine(message);
일반적으로 string.Format형식보다 $" " 형식을 선호함
switch case 와 enum 타입
switch case문의 case에는 변수를 넣을 수 없고 고정된 값(상수)만 가능하다.
가독성은 좋지만 if문 보다 제한되는 상황이다 보니, case에 들어가는 상수는 의미를 갖는 enum형을 넣는 것이 좋다.
enum형은 변수가 수십개씩 늘어나다 보면 변수명이 겹치지않게 신경쓰이는 경우를 해결해준다.
위 상황처럼 임의의 문자열에 상수를 순서대로 부여해서 switch case에 사용할 수도 있다.
enum타입에서 꺼내온 변수들은 정수가 아니라 enum타입이기 때문에
case에 쓸 때는 바로 변수명을 입력하는게 아니라 (int)를 붙여줘야 한다.
그리고 바로 사용하면 enum에서 가져온 지 모르니까 최종적으로 (int)enumType.문자열을 해야한다.
즉, enum(열거형)은 기본적으로 상수 값을 그룹화하는 특별한 타입이라고 말할 수 있다.
각 enum 멤버는 상수이며, 프로그램에서 변경할 수 없다.
ref와 out
static void Divide(int a, int b, out int result1, out int result2)
{
result1 = a/b;
result2 = a%b;
}
static void main()
{
int num1 = 10;
int num2 = 3;
int result1;
int result2;
Divide(10, 3, out result1, out result2);
}
C#에는 포인터 개념이 없는 대신 ref를 이용해서 값의 참조를 할 수 있다.
보통 함수에서 받은 매개변수를 복사하기 때문에 실제 값은 변화가 없지만, ref를 사용하면 실제로 값을 변화시킨다.
out은 함수에서 계산한 결과를 해당 변수에 저장하겠다는 의미이다.
오버로딩
static int Add(int a, int b, int c=0, float d=1.0f)
{
return a+b+c;
}
static void main()
{
class.Add(1, 2, d:2.0f)
}
구조체
struct Mage {
public int hp;
public int attack;
}
나중가면 함수에 넣어야 할 매개변수가 수십개는 되는데, 변수를 하나하나 함수에 다 넘길 필요 없이
대장 구조체 하나만 넘기면 간단하니 구조체를 사용한다.
그냥 변수 담는 바구니임
클래스
함수를 중심으로 하는 절차(프로시저)지향, 클래스 중심의 객체지향 프로그래밍이 있다.
절차 지향으로 프로그래밍을 하면 함수 안에 함수를 호출하는 등 상속 관계가 엉망이 되서 유지보수가 힘들어진다.
클래스 중심의 객체 지향으로 관리하게 되면 해당 객체만 고치면 되니 유지보수가 편하다.
클래스는 붕어빵 틀 같은 설계도 개념이라 생각하자.
class는 ref 참조이므로,
main에서 함수의 파라미터 값으로 인스턴스를 넣어줘서 값 변경 시
ref로 굳이 명시하지 않아도 실제 인스턴스 값이 변한다.
하지만 struct는 복사이므로 값이 변하지 않는다.
생성자
생성자는 클래스 이름() 만 쓰고 아무것도 쓰지 않는다.
처음 객체를 생성할 때 초기화에 사용한다.
보통 Class name = new Class(); 형식으로 생성자를 통해 초기화하는데
public Knight Clone()
{
Knight kight = new Knight();
knight.hp = hp;
knight.attack = attack;
return knight;
}
이런 형식으로 함수로 초기화할 수도 있다.
Knight.Clone
앞서 말했던 오버로딩이 가능하다.
초기화해야 할 변수들이 나중가면 몇십개씩 되는데 이럴 때 사용할 수 있다.
public Knight(int hp) : this()
{
this.hp = hp;
}
public Knight(int hp, int attack) : this()
{
this.hp = hp;
this.attack = attack;
}
여기에서 this()는 매개변수로 받지 않은 나머지 필드들은 처음 만들었던 생성자에 초기화된 값을 사용한다는 의미이다.
static
static이란 알다싶이 정적이란 의미인데, 그래서 뭐가 정적이냐고 물어볼 수 있다.
각각의 인스턴스에 따라 필드는 달라질 수 있다.
만약 필드가 static 으로 선언된다면 전역변수로 객체들이 값을 공유하므로, 인스턴스에 따라 다른 값이 아니라 class에 따라 값을 가질 수 있게 된다.
간단하게 인스턴스별로 존재하는 것이 아니라 클래스에 오로지 한개만 존재한다.
클래스에 종속되는가, 클래스의 인스턴스에 종속되는가 차이임
클래스 내의 함수에 붙는 static은 애초에 클래스에 종속이든 아니든 함수를 바꿀 수 없는데, 이게 무슨 의미를 갖나면
static으로 선언된 함수의 내부에서는 외부의 필드를 건드릴 수 없다.
왜냐하면 static으로 선언된 함수는 결국 클래스의 공용함수인데
공용함수에서 개개인의 인스턴스에 있는 고유 필드에 접근할 수 없기 때문이다.
모든 인스턴스마다 필드값이 다를 수 있는데
이 공용함수가 어떤 인스턴스를 참조해야 될지를 알 수 없다.
그래서 static 함수 안에서는 static으로 선언된 변수만 연산할 수 있다.
오해하면 안되는게 static을 붙여도 일반 인스턴스에 접근 가능하다.
하지만 접근하기 위해서 함수 안에 새로 생성자를 통해 인스턴스를 생성하고 그 인스턴스의 필드에 접근한다.
static은 클래스에 종속됐으므로 클래스.static 함수로 바로 호출 가능하지만, 다른 함수는 인스턴스.함수로 접근해야 한다.
string
string name = "Harry Potter";
// 1. 찾기
bool found = name.Contains("Harry");
int index = name.IndexOf('z'); // -1 반환
// 2. 변형
name = name + " Junior";
string lowerCaseName = name.ToLower();
string upperCaseName = name.ToUpper();
string newName = name.Replace('r', 'l');
// 3. 분할
string[] names = name.Split(new char[] { ' ' });
string substringName = name.Substring(5);
레퍼런스
'언어 > C#' 카테고리의 다른 글
property 소개 (0) | 2024.07.08 |
---|---|
추상 클래스와 인터페이스 (0) | 2024.07.08 |
generic 타입 소개와 object, var 대신 사용하는 이유 (0) | 2024.07.08 |
배열과 리스트, 딕셔너리 (2) | 2024.07.04 |
객체지향의 원칙 상속성, 은닉성, 다형성 (0) | 2024.07.03 |