C#/C# 인터페이스

ICollection : 컬렉션 인터페이스

로파이 2021. 11. 13. 17:34

ICollection 인터페이스

컬렉션을 구성하는 size, enumerators 그리고 동기화 기법을 정의한다.

기본적으로 IEnumerable을 상속한 인터페이스

public interface ICollection : System.Collections.IEnumerable

 

ICollection 인터페이스는 IEnumerable을 확장하였다. IDictionary와 IList는 ICollection을 확장한 특별한 인터페이스이다. IDictionary는 key/value 쌍을 가지는 컬렉션을 구현한 형태이고, IList는 인덱스로 랜덤 접근이 가능한 컬렉션을 구현한다.

 

Queue나 Stack의 경우 Collection을 바로 구현하여 List와 같이 랜덤 접근이 불가능하다. IDictionary 인터페이스 혹은 IList 인터페이스를 구현하고 싶지 않은 경우 Collection을 상속하여 새로운 컬렉션을 정의하도록 한다.

public interface ICollection : IEnumerable
{
    int Count { get; }
    bool IsSynchronized { get; }
    object SyncRoot { get; }
    void CopyTo(Array array, int index);
}

- 프로퍼티

  • Count  : 현재 컬렉션이 가지고 있는 원소 개수를 반환한다.
  • IsSynchronized : 현재 컬렉션이 동기화 기법을 사용하여 스레드 안전한지 확인한다.
  • SyncRoot : 현재 컬렉션을 스레드 안전하게 접근할 수 있도록 동기화에 사용되는 객체로 이용할 수 있다.

- 메서드

  • CopyTo : 일차원 Array 클래스에 Collection의 모든 원소를 복사할 수 있다.

 

일반화된 Collection<T>

public interface ICollection<T> : IEnumerable<T>, IEnumerable
{
    int Count { get; }
    bool IsReadOnly { get; }
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    bool Remove(T item);
}

- 프로퍼티

  • Count : 해당 컬렉션의 원소 개수를 반환한다.
  • IsReadOnly : 해당 컬렉션이 ReadOnly 타입인지 반환한다.

- 메서드

  • void Add(T item) : T 타입의 아이템을 추가한다.
  • void Clear() : 컬렉션의 모든 원소를 제거한다.
  • void Contains(T item): 해당 원소가 있는지 확인한다.
  • void CopyTo(T[] array, int arrayIndex) : array 배열에 컬렉션의 모든 원소를 복사한다.
  • bool Remove(T item) :  해당 원소가 있다면 true를 반환하고 원소를 제거한다. 없어서 실패했다면 false를 반환한다.

 

일반화 컬렉션이 더 자주 사용되므로 Collection<T>를 상속한 자료구조를 살펴보자.

 

IList<T> 인터페이스

ICollection<T>, IEnumerable<T>, IEnumerable을 상속한 인터페이스로 리스트 형태의 자료구조 인터페이스를 제공한다.

- 프로퍼티

  • this[int index] : 인덱서, 원소를 인덱스로 랜덤 접근가능한 프로퍼티

- 메서드

  • IndexOf(T item) : 특정 아이템이 해당 컬렉션에 몇번째 인덱스인지 반환한다. -1이라면 해당 아이템이 없다는 뜻이다.
  • Insert(int index, T item) : 해당 인덱스에 item을 삽입하는 메서드. 해당 인덱스가 유효하지 않다면, ArgumentOutOfRangeException을 던진다.
  • RemoveAt(int index) : 해당 인덱스에 있는 원소를 제거한다. 유효하지 않는 인덱스에 대해 ArgumentOutOfRangeException 예외를 던진다.

 

IDictionary<T> 인터페이스

namespace System.Collections.Generic
{
    [DefaultMember("Item")]
    public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable where TKey : notnull
    {
        TValue this[TKey key] { get; set; }
        ICollection<TKey> Keys { get; }
        ICollection<TValue> Values { get; }

        bool ContainsKey(TKey key);
        bool Remove(TKey key);
        bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value);
    }
}

- 프로퍼티

  • TKey 타입의 key를 이용하여 인덱서처럼 TValue를 접근할 수 있다. 해당 Key가 존재하지 않으면 KeyNotFoundException 예외를 던진다.
  • Keys, Values : 해당 프로퍼티를 통해 소유하고 있는 모든 Key와 Value에 대한 컬렉션을 받을 수 있다.

- 메서드

  • bool ContainsKey(TKey key) : 해당 키를 가지고 있다면, true를 반환한다.
  • bool Remove(TKey key) : 해당 키에 해당하는 원소를 삭제한다. 해당 키가 없다면 false를 반환하고 삭제가 성공하였다면true를 반환한다.
  • bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value); : 해당 키가 있는지 확인하고 있다면 value에 값을 채워 반환한다. 결과는 성공 유무에 따라 true/false를 반환

 

예제) Dictionary 다루기

원소의 타입은 KeyValuePair<TKey, TValue>을 가진다.

어떤 상황에서 예외가 발생하는 지 주의한다.

public class DictionaryExample
    {
        static public void Run()
        {
            // Create a Dictionary of string key - string value
            IDictionary<string, string> openWith = new Dictionary<string, string>();

            // Dictionary에 key-value 쌍을 추가한다.
            openWith.Add("txt", "notepad.exe");
            openWith.Add("bmp", "paint.exe");
            openWith.Add("dib", "paint.exe");
            openWith.Add("rtf", "wordpad.exe");


            // Add 메서드는 새로운 키가 이미 있다면 exception을 던진다.
            try
            {
                openWith.Add("txt", "winword.exe");
            }
            catch(ArgumentException)
            {
                Console.WriteLine("An element with key = \"txt\" already exists.");
            }

            // 인덱서를 이용하여 key 값을 통해 value를 얻을 수 있다.
            // 키가 없다면 System.Collections.Generic.KeyNotFoundException 예외를 던진다.
            try
            {
                Console.WriteLine("For key \"png\" value = {0}", openWith["png"]);
            }
            catch(KeyNotFoundException)
            {
                Console.WriteLine("Threre is no key \"png\" in dictionary");
            }

            // 인덱서를 이용하여 존재하는 key에 대한 value를 읽을 수도 있지만,
            // 해당 value를 변경할 수도 있다.
            openWith["rtf"] = "winword.exe";
            Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);

            // 키가 존재하지 않는다면 새로운 키에대한 값을 대입할 수 있다.
            openWith["doc"] = "winword.exe";

            // 예외를 던지지 않고 존재하는지 먼저 확인후 value를 가져올 수 있다.
            string value = "";
            if(openWith.TryGetValue("tif", out value))
            {
                Console.WriteLine("For Key = \"tif\", value = {0}", value);
            }
            else
            {
                Console.WriteLine("Key= \"tif\" is not found.");
            }

            // ContainsKey can be used to test keys before inserting them
            if(!openWith.ContainsKey("ht"))
            {
                openWith.Add("ht", "hypertrm.exe");
                Console.WriteLine("Value Addedforkey \"ht\": {0}", openWith["ht"]);
            }

            // Dictionary 컬렉션을 foreach를 사용하여 순회할 수 있으며
            // KeyValuePair<Key,Value>를 이용하여 원소를 캡쳐할 수 있다.
            foreach(KeyValuePair<string, string> kvp in openWith)
            {
                Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
            }

            // Key만 가지고 컬렉션을 구성하거나 Value만 가지고 컬렉션을 구성할 수 있다.
            ICollection<string> keys = openWith.Keys;
            Console.WriteLine();
            foreach(string s in keys)
            {
                Console.WriteLine("Key = {0}", s);
            }

            ICollection<string> values = openWith.Values;
            Console.WriteLine();
            foreach(string s in values)
            {
                Console.WriteLine("Value = {0}", s);
            }

            Console.WriteLine("\nRemove(\"doc\")");
            openWith.Remove("doc");

            if(!openWith.ContainsKey("doc"))
            {
                Console.WriteLine("Key \"doc\" is not found");
            }
        }

    }