배열의 선언과 사용
데이터형식[] 이름 = new 데이터형식[용량];
- 5개 원소를 가지는 배열
int[] scores = new int[5];
배열은 메모리가 연속적인 구조를 갖는다. 배열은 다음과 같이 3가지 방법으로 초기화할 수 있다.
string[] array1 = new string[3] { "hello", "안녕", "Halo" };
string[] array2 = new string[] { "안녕", "Hello", "Halo" };
string[] array3 = { "안녕", "Hello", "Halo" };
^연산자를 이용한 배열 인덱스 접근
- c#8.0에서부터 ^숫자는 인덱스를 지칭하게 된다. 실제 타입은 System.Index 형식이다.
- ^숫자 = 배열.Length - 숫자의 의미를 가진다.
string ss = array1[^1]; // array1[array1.Length - 1] / "Halo"
System.Array 형식
모든 배열은 System.Array 형식에서 파생된 객체이다. System.Array 객체가 제공하는 특성과 메서드를 사용하면 배열을 가지고 다양한 작업을 할 수 있다.
정적 메서드
- Sort() - 정렬
- BinarySearch<T>() - 이진 탐색
- IndexOf() - 값 기반 인덱스 탐색
- TrueForAll<T>() - 모든 값이 조건이 부합하는 지 체크 (bool)
- FindIndex<T>() - 특정 조건을 만족하는 첫번째 요소의 인덱스 찾기
- Resize<T>() - 배열의 사이즈를 재조정
- Clear<T>() - 배열의 요소를 초기화
- ForEach<T>() - 배열의 각 요소에 대해 특정 작업을 시행
- Copy<T>() - 배열의 일부분을 다른 배열에 복사
인스턴스 메서드
- GetLength() - 배열의 크기를 반환
프로퍼티
- Length : 배열의 길이를 반환
- Rank : 배열의 차원을 반환
외에도 많은 메서드와 프로퍼티가 있다.
System.Array에 정의된 ForEach<T>를 보면 Action<T>라는 것을 입력 인자로 받는데 Action<T>는 T형식의 인자를 매개변수로 하나 갖는 함수 객체로 델리게이트로 선언되어 있다.
//
// 요약:
// Performs the specified action on each element of the specified array.
//
// 매개 변수:
// array:
// The one-dimensional, zero-based System.Array on whose elements the action is
// to be performed.
//
// action:
// The System.Action`1 to perform on each element of array.
//
// 형식 매개 변수:
// T:
// The type of the elements of the array.
//
// 예외:
// T:System.ArgumentNullException:
// array is null. -or- action is null.
public static void ForEach<T>(T[] array, Action<T> action);
//
// 요약:
// Encapsulates a method that has a single parameter and does not return a value.
//
// 매개 변수:
// obj:
// The parameter of the method that this delegate encapsulates.
//
// 형식 매개 변수:
// T:
// The type of the parameter of the method that this delegate encapsulates.
public delegate void Action<in T>(T obj);
ex)
class Program
{
static void PrintArray(System.Array array)
{
foreach(var e in array)
{
Console.Write(e);
}
Console.WriteLine();
}
static void Main(string[] args)
{
int[] scores = new int[30];
Random rand = new Random();
for (int i = 0; i < scores.Length; ++i)
{
scores[i] = rand.Next(0, 50);
}
Array.ForEach<int>(scores, new Action<int>(Print));
}
}
배열 분할하기
C# 8.0에 도입된 System.Range 형식을 이용하면 슬라이스된 배열을 얻을 수 있다.
int[] sliced = array[a..b];
sliced 배열은 [a,b) 인덱스 범위의 원소를 갖는 배열로 b가 포함되지 않으며 a를 생략시 원소 처음부터 b를 생략시 끝 원소까지 포함한다.
int[] IndexedArray = array1[..^1]; // 맨 뒤 원소를 제외하고 만든 배열
2차원 배열의 선언
데이터 형식[,] 배열이름 = new 데이터형식[2차원 길이, 1차원 길이]
ex) 2x3의 배열
int[,] arr = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr2 = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };
int[,] arr3 = { { 1, 2, 3 }, { 4, 5, 6 } };
2차원 배열의 타입은 [,]으로 지칭되며 C/C++과 달리 [][]로 선언되지 않는다.
[][]는 가변 배열을 의미한다.
가변 배열
가변 배열은 가변 길이의 배열을 요소로 담는 배열을 의미한다. C++에서 포인터 배열과 같은 의미를 갖는다.
데이터형식[][] 배열이름 = new 데이터형식[가변 배열의 용량][];
변수 선언에서 [][]을 붙이는 것이 특징이다.
- 구성하는 원소의 배열 길이가 같지 않아도 되기 때문에 가변 배열이라고 부른다.
// 가변 배열 (포인터 배열)
int[][] jagged = new int[3][];
jagged[0] = new int[5] { 1, 2, 3, 4, 5 };
jagged[1] = new int[] { 1, 2, 3 };
jagged[2] = new int[] { 3, 4, 5 };
컬렉션
https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/collections
C#에서 배열보다 좀 더 유연한 자료구조 (큐, 스택, 리스트)와 같은 클래스를 System.Collections에서 제공하고 있다. System.Collections에서 제공하는 자료구조는 object 형식을 상속하는 원소를 담을 수 있으므로 임의의 타입 원소를 보관할 수 있다.
- System.Collections
ArrayList | 필요에 따라 크기가 동적으로 증가하는 개체 배열을 나타냅니다. |
Hashtable | 키의 해시 코드에 따라 구성된 키/값 쌍의 컬렉션을 나타냅니다. |
Queue | FIFO(선입선출) 방식의 개체 컬렉션을 나타냅니다. |
Stack | LIFO(후입선출) 방식의 개체 컬렉션을 나타냅니다. |
System.Collections 타입은 배열과 다르게 임의의 원소 접근 (Container[index] - 인덱싱)을 통한 요소 접근이 불가능 하다.
ArrayList
Add(), Insert, RemoveAt과 같은 메서드를 제공하고 배열을 이용하여 ArrayList를 초기화할 수 있다.
ArrayList FromArray = new ArrayList() { 1, 2, 3, 4, 5, 6 };
배열을 이용한 초기화는 ArrayList가 IEnumerable 인터페이스를 상속하고 Add() 메서드를 구현했기 때문이다.
Queue와 Statck은 IEnumerable를 상속했지만 Add() 메서드를 구현하지 않았기 때문에 배열을 이용한 초기화가 불가능하다.
Queue
FIFO 특징을 갖는 자료구조로 Enqueue()와 Dequeue()를 통해 원소를 뒤에 추가하고 맨 앞의 원소를 꺼내올 수 있다.
Queue que = new Queue();
que.Enqueue(1);
que.Enqueue(3);
var front = que.Dequeue();
Stack
LIFO 특징을 갖는 자료구조로 Push()와 Pop()를 통해 뒤에 원소를 추가하고 맨 뒤의 원소를 꺼내올 수 있다.
Stack stack = new Stack();
stack.Push(1);
int top = (int)stack.Pop();
Hashtable
키-값 쌍을 갖는 해쉬 테이블 자료구조이다. {키, 값} 쌍으로 초기화할 수 있다.
Hashtable table = new Hashtable { { "이름", "김아무개" }, { "나이" , 26} };
인덱서
C/C++에서 operator[](int index)를 오버로딩한 것과 같으며 임의의 인덱스에 접근하는 방법을 제공한다.
프로퍼티와 비슷하게 get과 set에 대한 행동을 정의할 수 있다.
한정자 인덱서형식 this[형식 index]
{
get { // index에 해당하는 원소 반환}
set { // index에 해당하는 원소 변경}
}
foreach가 가능한 컨테이너 클래스 만들기
foreach 문으로 각 원소를 순회하고 싶다면 IEnumerable 인터페이스를 상속하여 boolean MoveNext() / void Reset() / Object Current{get;}을 구현해야한다.
IEnuerable
- MoveNext() : 다음 원소의 위치로 이동한다.
- Reset() : 현재 위치를 컨테이너의 맨 처음 위치의 앞으로 이동한다. 0이 시작이라면 -1에 위치해야하며 MoveNext()로 인해 -1에서 0으로 증가한 다음 맨 처음 요소를 접근한다.
- Current 프로퍼티 : 현재 위치의 원소를 반환한다.
IEnumerator
- GetEnumerator() : 위 3 메소드를 이용하여 원소를 탐색하는 대리자 객체를 반환한다. 단순히 this를 반환하면 된다.
- 예시
class MyContainer : IEnumerable, IEnumerator
{
private int currentSize = 10;
private int[] array;
private int pos = -1;
public MyContainer() { array = new int[currentSize]; }
public int this[int index]
{
get { return array[index]; }
set
{
if (index >= array.Length)
{
currentSize = 2 * currentSize;
Array.Resize<int>(ref array, currentSize);
}
array[index] = value;
}
}
public int GetLength() { return currentSize; }
public object Current { get { return array[pos]; } }
public bool MoveNext()
{
if (pos == array.Length - 1)
{
Reset();
return false;
}
++pos;
return (pos < array.Length);
}
public void Reset()
{
pos = -1;
}
public IEnumerator GetEnumerator()
{
return this;
}
}
'C# > C# 기본' 카테고리의 다른 글
[C#] 대리자와 이벤트 (0) | 2021.10.15 |
---|---|
[C#] Generic 프로그래밍 / 예외 처리 (0) | 2021.10.12 |
[C#] 프로퍼티 (0) | 2021.10.08 |
[C#] 인터페이스와 추상 클래스 (0) | 2021.10.07 |
[C#] 클래스 (0) | 2021.10.06 |