외로운 Nova의 작업실

c# 언어 공부 - 6(배열, 가변배열, 컬렉션, 인덱서) 본문

Programming/C#

c# 언어 공부 - 6(배열, 가변배열, 컬렉션, 인덱서)

Nova_ 2022. 5. 4. 23:38

안녕하세요. 오늘은 저번시간에 이어 c#의 배열에 대해서 알아보겠습니다.

배열은 c언어와 개념이 똑같습니다.

고로 생성하는 법을 알아보겠습니다.

코드를 보시죠

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4]; //크기가 4인 배열 생성
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;
        }

    }

    
   
}

위 코드처럼 배열을 생성할 수 있습니다.

c#은 특별하게 ^연산자가 있습니다.

배열의 마지막이라는 뜻을 가지고있습니다.

예를들어 ^1은 배열의 마지막에서 첫번째를 의미하는 수입니다.

코드를 보시죠.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4];
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;

            Console.WriteLine($"{score[^1]}"); //score의 마지막에서 첫번째 값을 의미 즉 3을 출력
        }

    }

    
   
}

위의 코드는 3을 의미합니다.

^연산자를 담는 변수를 만들려면 System.Index 자료형을 이용합니다.

코드를 보시죠.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4];
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;

            System.Index last = ^1;

            Console.WriteLine($"{score[last]}"); //콘솔에는 3이 출력됩니다.
        }

    }

    
   
}

위 코드와 같이 사용됩니디.

 

<배열을 초기화하는 세가지 방법>

1. 배열 용량을 명시하는 방법

  int[] score = new int[2]{1, 2};

2. 배열 용량을 생략하는 방법

  int[] score = new int[]{1, 2};

c언어와 마찬가지로 자동으로 배열의 크기를 할당해줍니다.

3. 연산자 대괄호 생략하는 법

int[] score = {1, 2}

우리가 많이 보던 c언어에서의 배열선업법입니다.

위세가지는 모두 동일한 기능을 합니다.

 

<System.Array>

c#에서는 배열도 객체입니다.

모든 배열들은 System.Array 클래스 형식에서 파생되었습니다.

따라서 System.Array 클래스의 메소드를 알면 배열을 관리하고 응용하기 편합니다.

sort() 배열을 정렬합니다.
BinarySearch<T>() 이진탐색을 수행합니다.
IndexOf() 데이터의 인덱스를 반환합니다.
TrueForAll<T>() 지정한 조건에 부합하는지 여부를 반환합니다.
FindIndex<T>() 배열에서 조건에 부합하는 첫번째 인덱스를 반환합니다.
Resize<T>() 배열의 크기를 재조정합니다.
Clear() 배열의 모든요소를 초기화합니다.
ForEach<T>() 배열의 모든요소에대해 동일한 작업을 수행합니다.
Copy<T>() 배열의 일부를 다른 배열에 복사합니다.
GetLength() 배열에서 지정한차원의 길이를 반환합니다.
Length 배열의 길이를 반환합니다.
Rank 배열의 차원을 반환합니다.

 

<배열 분할하기>

배열의 일정부분을 나누어서 또 다른 배열을 만드는것을 배열의 분할이라고합니다.

위 메소드를 사용하여 분할을 구현해보겠습니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4];
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;

            int[] slice = new int[2];

            Array.Copy(score, 0, slice, 0, 3); //slice의 0~2번째 인덱스를 slice에 복사함
        }

    }

    
   
}

 

위 코드처럼 Copy 메소드를 사용하는 것도 좋지만 더 간결하게 쓸 수 있는 방법이 있습니다.

바로 System.Range를 활요하면됩니다.

System.Range는 ^연산자와 비슷하게 ..연산자를 사용합니다.

코드부터 보시죠.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4];
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;

            int[] slice = new int[2];

            slice = score[0..2];//score의 0번째 인덱스부터 1번째 인덱스까지 slice에 넣음
        }

    }

    
   
}

위 코드처럼 분할 할 수 있습니다.

0..2를 변수에 넣을 경우 변수의 클래스는 System.Range입니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] score = new int[4];
            score[0] = 0;
            score[1] = 1;
            score[2] = 2;
            score[3] = 3;

            int[] slice = new int[2];

            System.Range range = 0..2;

            slice = score[range];
        }

    }

    
   
}

Range를 사용할때 주의할점은 0..2는 0번째 인덱스부터 1번째 인덱스까지 값을 표현합니다.

따라서 위 코드의 slice에는 0,1이 slice에 들어가게됩니다.

 

<2차원 배열>

c언어와 마찬가지로 c#에도 2차원 배열이 있습니다.

코드부터보시죠.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[, ] score = new int[2, 2]; //2차원 배열 생성
            score[0, 0] = 0;
            score[0, 1] = 1;
            score[1, 0] = 2;
            score[1, 1] = 3;

        }

    }

    
   
}

c언어와 다르게 c#은 []대괄호 중간에 ,를 넣어서 생성하게됩니다.

생성과 동시에 초기화하고싶다면 아래와 같은 코드로 가능합니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[, ] score = new int[2, 2] { { 1, 2 } , { 3, 4} };
       

        }

    }

    
   
}

또한 이렇게 2차원 이상의 다차원배열을 만들 수있지만 3차원부터는 복잡하기때문에 안만드는것을 추천합니다.

 

<가변배열>

2차원 배열에서는 배열자체를 얻지못하고 배열의 값을 얻을 수 있습니다.

즉 score의 배열에서 첫번째 배열인 {1, 2}를 얻지 못하고 배열의 값인 1과 2만 얻을 수 있습니다.

하지만 가변배열은 {1, 2}를 얻을 수 있습니다.

생성은 c언어와 비슷합니다.

코드부터보시죠.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[][] score = new int[2][2]; //가변배열 score 생성
       

        }

    }

    
   
}

가변배열은 꼭 new를 붙여서 생성해줘야합니다.

 

<컬렉션 맛보기>

컬렉션이란 같은 성격을 띈 데이터의 모음을 담는 자료구조를 말합니다.

예를 들어, ArrayList, Queue, Stack, Hashtable이 있죠.

4가지를 간단하게 설명해보도록 하겠습니다.

 

ArrayList 컬렉션은 배열과 가장 닮은 컬렉션입니다.

하지만 배열은 크기를 고정시켜서 사용하지만 ArrayList는 크기와 관계없이 배열처럼 사용이가능합니다.

메소드는 Add(), RemoveAt(), Insert() 이렇게 3개 있습니다.

Add()는 컬렉션의 가장 마지막에 있는 요소뒤에 새요소를 추가 하고,

RemoveAt() 메소드는 특정 인덱스에 있는 요소를 제거합니다.

Insert()메소드는 원하는 위치에 새요소를 삽입합니다.

사용방법은 아래와 같습니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            ArrayList list = new ArrayList();
            list.Add(12);
            list.Add(13);
            list.Add(14);

            list.RemoveAt(2); //14를 삭제

            list.Insert(1, 30); //12와 13사이에 30을 넣음 즉, 인덱스 1에 30을 넣음
       

        }

    }

    
   
}

 

<Queue>
Queue는 FIFO구조입니다.

첫번째로 들어가면 처번째로 나온다는 것이지요.

예를 들어 줄스는 것과 같습니다.

첫번째로 줄슨사람이 첫번째로 나가는 것입니다.

중요한 메소드는 Enqueue() 입력과 Dequeue() 출력 메소드입니다.

아래는 사용 방법입니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Queue list = new Queue();
            list.Enqueue(13);
            list.Enqueue(14);

            Console.WriteLine(list.Dequeue()); //13을 출력함


        }
    }
}

 

<Stack>

스택은 LIFO입니다. Last In First Out입니다.

마지막으로 들어간 사람이 첫번째로 나오는 것입니다.

예를 들어 승강기에 사람들이 막 들어가는데, 삐삐 울려서 마지막에 탄사람이 나오는 것과 같습니다.

가장 중요한 메소드는 Push() 스택에 넣는 메소드와, Pop() 스택에서 나오는 메소드입니다.

사용방법은 아래와 같습니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Stack list = new Stack();
            list.Push(13);
            list.Push(14);

            Console.WriteLine(list.Pop()); // 14가 출력 됨


        }
    }
}

 

<Hashtable>

Hashtable은 쌍으로 이루어진 데이터를 다룰 때 사용합니다.

영어사전이 좋은 예시입니다.

"책" 을 검색하면 "book"이 나오는 것처럼 말입니다.

사용방법은 아래와 같습니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Hashtable dictionary = new Hashtable();

            dictionary["책"] = "book";
            dictionary["요리"] = "cook";

            Console.WriteLine(dictionary["책"]); //book 출력


        }
    }
}

 

<컬렉션 초기화방법>

컬렉션을 처음에 값이 들어있는 상태로 초기화하는 방법은 배열을 사용하여 구현할 수 있습니다.

코드먼저 봅시다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] arr = { 123, 456, 789 }; //앞 인덱스부터 순서대로 자료구조로 들어감

            ArrayList list = new ArrayList(arr);
            Queue queue = new Queue(list);
            Stack stack = new Stack(list);

            Console.WriteLine(stack.Pop()); // 789가 출력


        }
    }
}

Hashtable 같은경우엔 좀 다르게 초기화합니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Hashtable dictionary = new Hashtable()
            {
                { "책", "book"},
                { "요리", "cook" }
            };


        }
    }
}

 

 

<인덱서>

인덱서는 클래스안의 배열요소를 배열을 사용하듯이 해주는 메소드입니다.

코드부터 보시죠.

class score
        {
            private int[] array;
            public score()
            {
                array = new int[10];
            }

            public int this[int index] //인덱서 메소드
            {
                get
                {
                    return array[index];
                }
                set
                {
                    array[index] = value;
                }
            }
        }

위 코드처럼 클래스를 구현하게되면 클래스를 배열처럼 쓰게되고 실제로 바뀌는건 socre안의 배열 array배열입니다.

사용법은 아래와같습니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Score score = new Score();

            score[0] = 2;
            score[1] = 3;

            Console.WriteLine(score[1]); //3을 출력함

        }

        class Score
        {
            private int[] array;
            public Score()
            {
                array = new int[10];
            }

            public int this[int index]
            {
                get
                {
                    return array[index];
                }
                set
                {
                    array[index] = value;
                }
            }
        }
    }
}

 

<foreach가 가능한 객체만들기>

배열은 foreach가 가능하지만 인덱서로 만든 배열은 foreach가 불가능합니다.

foreach가 가능한 클래스는 IEnumerabler과 IEnumerator 인터페이스를 상속한 클래스만이 가능합니다.

다르게 말하면 foreach에서 쓰는 규칙은 IEnumerable과 IEnumerator 규칙입니다.

즉, 인덱서로 만든 배열에 IEnumerable과 IEnumerator 인터페이스를 상속하게되면 foreach구문을 사용 할 수 있습니다.

IEnumerable에서 구현해야하는 메소드는 아래 하나입니다.

GetEnumerator() 자기자신(IEnumerator)을 반환

IEnumerator에서 구현해야하는 메소드는 아래 3개입니다.

boolean MoveNext() 다음요소로 이동합니다.
void Reset() 컬렉션의 첫번째의 위치의 앞으로 이동합니다.
Object Current{get;} 컬렉션의 현재 요소를 반환합니다.

의 4가지를 구현해보겠습니다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Score score = new Score();

            score[0] = 2;
            score[1] = 3;

            Console.WriteLine(score[1]); //3을 출력함

        }

        class Score : IEnumerable, IEnumerator
        {
            private int[] array;
            int position = -1; //IEnumerable인터페이스를 구현하기위해 사용하는 필드 첫시작은 -1이여야 요소를 넣을 때 그다음 인덱스 0부터시작
            public Score()
            {
                array = new int[10];
            }

            public int this[int index]
            {
                get
                {
                    return array[index];
                }
                set
                {
                    array[index] = value;
                }
            }

            public object Current //프로퍼티
            {
                get
                {
                    return array[position];
                }
            }

            public bool MoveNext()
            {
                if(position == array.Length - 1) //마지막 요소라면 false 반환
                {
                    Reset();
                    return false;
                }

                position++;
                return (position < array.Length);// 이동이 성공햇다면 true반환
            }

            public void Reset()
            {
                position = -1;
            }

            public IEnumerator GetEnumerator()
            {
                return this; //해당 클래스를 반환
            }
        }
    }
}

위 코드처럼 상속하고 구현하게되면 Score클래스는 foreach문을 사용할 수 있게됩니다.

 

하지만 너무 길죠?

그래서 간단하게 구현할 수 있는게 있습니다.

바로 yield 키워드를 사용하는 것입니다.

코드를 봅시다.

using System;
using System.Collections;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            Score score = new Score();


        }

        class Score 
        {
            private int[] array;
            int position = -1; //IEnumerable인터페이스를 구현하기위해 사용하는 필드 첫시작은 -1이여야 요소를 넣을 때 그다음 인덱스 0부터시작
            public Score()
            {
                array = new int[3] {1, 2, 3};
            }

            public IEnumerator GetEnumerator()
            {
                yield return array[0];
                yield return array[1];
                yield return array[2];
                yield break; 
            }
        }
    }
}

이처럼 GetEumerator()메소드만 구현해놓고 그 안에 yeild 키워드를 이용하면 컴파일러가 자동으로 상속된 클래스를 만들어줍니다.

또한 배열의 값 전달이 끝낫다면 break문으로 끝내줘야합니다.

 

다음시간에는 일반화 프로그래밍에대해서 알아보도록 하겠습니다.

Comments