CodeGym /행동 /C# SELF /딕셔너리 계약: IDictionary<TKey, TValue>

딕셔너리 계약: IDictionary<TKey, TValue>

C# SELF
레벨 28, 레슨 4
사용 가능

1. 들어가기

상상해봐: 너가 고양이 연구 비밀 연구소에 취직했어. 너한테 미션이 떨어졌지 — 고양이 이름으로 나이, 색깔, 꼬리 길이를 빠르게 알 수 있는 전자 딕셔너리를 만들어야 해. 이미 준비된 솔루션이 있어 — 바로 Dictionary<string, Cat>야. 근데 문제는, 나중에 상사가 말하길, 어떤 희귀 품종은 추가 순서를 기억해야 하고, 가끔은 특이한 방식으로 삭제도 해야 한대.

만약 너가 "고양이 딕셔너리"라면 뭐든 받을 수 있는 메서드를 짜야 한다면 어떨까? 구체적으로 어떤 클래스인지는 상관없고, 중요한 건 기본적인 "키-값" 연산만 지원하면 돼. 여기서 IDictionary<TKey, TValue> 인터페이스가 등장하는 거지.

IDictionary<TKey, TValue>는 .NET에서 딕셔너리가 뭔지 정의하는 공통 계약이야. 이건 구체적인 클래스가 아니라, 인터페이스 — 즉, 진짜 딕셔너리라면 반드시 따라야 하는 규칙 모음이야:

  • 키로 빠르게 찾기
  • "키-값" 쌍 추가, 삭제
  • 모든 쌍 순회
  • 키 존재 여부 확인

만약 중세에 Java가 있었다면, 기사들은 용과 싸울 때 칼 대신 키와 값을 휘둘렀을 거야. 그리고 C# 개발자들은 그냥 IDictionary<TKey, TValue> 구현했겠지 — 그러면 용(=네 코드)은 바로 끝장났을 거고.

2. IDictionary 인터페이스 표

이 인터페이스의 주요 "약속"(멤버)들을 한 번 보자:

인터페이스 멤버 타입 설명
this[TKey key]
속성 인덱서. 지정한 키에 연결된 값을 가져오거나 설정할 수 있어. 읽을 때: 키가 없으면 KeyNotFoundException이 터져. 쓸 때: 키가 없으면 새 쌍을 추가하고, 있으면 값을 업데이트해. 중요: 이게 딕셔너리 쓸 때 제일 많이 쓰는 방식이야.
ICollection<TKey> Keys
속성 딕셔너리 안의 모든 키를 담은 컬렉션을 반환해. 키만 순회할 때 써.
ICollection<TValue> Values
속성 딕셔너리 안의 모든 값을 담은 컬렉션을 반환해. 값만 순회할 때 써.
Add(TKey key, TValue value)
메서드 지정한 키와 값을 딕셔너리에 추가해. 이미 키가 있으면 ArgumentException이 터져.
ContainsKey(TKey key)
메서드 딕셔너리에 해당 키가 있는지 확인해. true면 찾은 거고, 아니면 false야. 인덱서로 없는 키에 접근해서 에러나는 걸 막을 때 아주 유용해.
Remove(TKey key)
메서드 지정한 키의 요소를 딕셔너리에서 삭제해. true면 성공적으로 찾고 삭제한 거고, 아니면 false(예: 키가 없을 때).
TryGetValue(TKey key, out TValue value)
메서드 지정한 키에 연결된 값을 가져와. 이건 안전하게 값을 얻는 방법이야, 키가 있는지 확실하지 않을 때. true면 키를 찾았고, value에 값이 들어가. 아니면 false고, value는 TValue의 기본값이야. 이 메서드는 예외를 던지지 않아서 실제 코드에서 엄청 많이 써.

3. IDictionary<TKey, TValue> 인터페이스의 주요 멤버

이제 제일 중요한 부분! IDictionary<TKey, TValue> 인터페이스 안에 뭐가 들어있는지 보고, 이 "계약"을 네 코드에서 어떻게 쓰는지 배워보자.


public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>
{
    TValue this[TKey key] { get; set; } // 키로 접근하는 인덱서
    ICollection<TKey> Keys { get; }
    ICollection<TValue> Values { get; }
    void Add(TKey key, TValue value); // 새 쌍 추가(키가 없어야 함) 
    bool ContainsKey(TKey key); // 이런 키가 있나?
    bool Remove(TKey key); // 키로 삭제
    bool TryGetValue(TKey key, out TValue value); // 안전하게 값 얻기
}

주요 포인트만 정리해보자:

인덱서 [key]

키로 값을 가져오거나 설정할 수 있어:

dictionary["Барсик"] = new Cat("Барсик", 2);

Keys와 Values 속성

딕셔너리의 모든 키 또는 모든 값을 컬렉션으로 얻을 수 있어.

foreach (var name in dictionary.Keys)
{
    Console.WriteLine(name);
}

Add, Remove, ContainsKey, TryGetValue 메서드

  • Add(key, value) — 새 쌍 추가
  • Remove(key) — 키로 삭제
  • ContainsKey(key) — 키가 있는지 확인
  • TryGetValue(key, out value) — 안전하게 값 얻기(키 없으면 예외 안 남)

4. IDictionary<TKey, TValue> 실전 사용법

범용 메서드

예를 들어, 너가 딕셔너리랑 작업하는 함수를 짜는데, 내부적으로 어떤 클래스인지 신경 쓰고 싶지 않아: 그냥 Dictionary일 수도 있고, SortedDictionary일 수도 있고, 누가 자기만의 "크립토딕셔너리"를 만들었을 수도 있지.

파라미터 타입을 IDictionary<TKey, TValue>로 선언하면 돼:


static void PrintDictionary<TKey, TValue>(IDictionary<TKey, TValue> someDictionary)
{
    foreach (var pair in someDictionary)
    {
        Console.WriteLine($"{pair.Key}: {pair.Value}");
    }
}

이제 네 메서드는 어떤 딕셔너리든 받을 수 있어! 내부적으로 뭐가 들어있든 상관없지 — 심지어 값이 암호화되는 딕셔너리여도, 네가 그렇게 재밌게 살고 있다면 말이야.

IDictionary로 파라미터 전달하기

예를 들어, 너가 고양이 관리 앱을 만들고 있는데, 네 메서드가 모든 종류의 설정 딕셔너리랑 동작하길 원해. 특정 클래스에만 묶이고 싶지 않다면 이렇게 하면 돼:


void SetCatParameters(IDictionary<string, string> parameters)
{
    if (parameters.ContainsKey("color"))
    {
        Console.WriteLine($"고양이 색칠: {parameters["color"]}");
    }
}

하나의 시그니처 — 여러 구현

표로 한 번 정리해볼게:

컬렉션 클래스 IDictionary<TKey,TValue> 구현 특징
Dictionary<TKey, TValue>
✅ 예 빠른 검색, 키 순서는 랜덤
SortedDictionary<TKey,TValue>
✅ 예 키가 자동으로 정렬됨
SortedList<TKey, TValue>
✅ 예 키 정렬, 메모리 절약형
(인터페이스 구현한 네 클래스) ✅ 예 네 마음대로 로직, 하지만 계약은 꼭 지켜야 함

5. 다른 컬렉션 인터페이스와의 관계

진짜 덕후들을 위해: IDictionary<TKey, TValue> 인터페이스는 ICollection<KeyValuePair<TKey, TValue>>랑 IEnumerable<KeyValuePair<TKey, TValue>>를 상속해. 즉, 모든 딕셔너리는:

  • foreach로 키-값 쌍 순회 가능,
  • 컬렉션 메서드로 쌍 추가/삭제 가능,
  • 요소 개수 얻기 가능.

foreach (var entry in myDictionary)
{
    Console.WriteLine($"{entry.Key} => {entry.Value}");
}

6. 특징과 흔한 실수

인덱서 사용

초보들이 제일 많이 하는 실수: 없는 키로 요소를 가져오려고 하면 KeyNotFoundException이 터져.


var value = myDictionary["НемаТакогоКлюча"]; // Boom!

그래서 항상 TryGetValue를 쓰는 게 좋아:


if (myDictionary.TryGetValue("Мурзик", out var cat))
{
    Console.WriteLine($"고양이 찾음: {cat}");
}
else
{
    Console.WriteLine($"고양이 못 찾았어!");
}

이미 있는 키 추가

이미 있는 키로 Add를 호출하면 ArgumentException이 터져. "추가 또는 업데이트"를 원하면 인덱서를 써:


// 없으면 추가, 있으면 업데이트
myDictionary["Мурка"] = new Cat("Мурка", 5);

순회와 수정

foreach 루프에서 딕셔너리를 직접 수정(예: 삭제)하려고 하면 예외가 나. 뭔가를 삭제하고 싶으면, 먼저 삭제할 키 목록을 따로 모아서, 그 다음에 따로 삭제해:


// 안전하게 요소 삭제하는 방법
var keysToRemove = new List<string>();
foreach (var pair in myDictionary)
{
    if (pair.Value.Age > 10) // 삭제 조건
    {
        keysToRemove.Add(pair.Key);
    }
}

foreach (var key in keysToRemove)
{
    myDictionary.Remove(key);
}
2
과제
C# SELF, 레벨 28, 레슨 4
잠금
딕셔너리에서 키 존재 여부 확인하기
딕셔너리에서 키 존재 여부 확인하기
1
설문조사/퀴즈
컬렉션 계약, 레벨 28, 레슨 4
사용 불가능
컬렉션 계약
컬렉션의 기본 인터페이스
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION