본문 바로가기
C#

[C#] KeyedCollection<TKey,TItem>, DictionaryBase

by DANEW 2023. 9. 30.

KeyedCollection<TKey,TItem>

 

KeyedCollection<TKey,TItem> 클래스 (System.Collections.ObjectModel)

키가 값에 포함되어 있는 컬렉션에 대한 추상 기본 클래스를 제공합니다.

learn.microsoft.com

namespace System.Collections.ObjectModel
{
    public abstract class KeyedCollection<TKey, TItem> : Collection<TItem>
    {
        protected KeyedCollection();
        protected KeyedCollection(IEqualityComparer<TKey> comparer);
        protected KeyedCollection(IEqualityComparer<TKey> comparer, int dictionaryCreationThreshold);
       
        public IEqualityComparer<TKey> Comparer { get; }
        protected IDictionary<TKey, TItem> Dictionary { get; }
       
        public bool Contains(TKey key);
        public bool Remove(TKey key);
 
        public TItem this[TKey key] { get; }
 
        protected void ChangeItemKey(TItem item, TKey newKey);
        protected abstract TKey GetKeyForItem(TItem item);
 
        protected override void InsertItem(int index, TItem item);
        protected override void SetItem(int index, TItem item);
        protected override void RemoveItem(int index);
        protected override void ClearItems();
    }
}

- 키 기반 컬렉션을 대표
- Collection<TItem>의 파생 클래스
- 추가된 기능: 키로 접근하는 능력
- 제거된 기능: 내부 목록의 프록시가 되는 능력

반응형


- 선형 목록과 헤시테이블을 결합
 * OrderedDictionary와 달리 IDictionary를 구현하지 않고, 키-값 쌍이라는 개념을 지원하지 않음
 * 요소에 키가 따로 있는 것이 아니라, 요소 자체에서 키를 얻음

- 기존 목록을 받는 생성자는 제공하지 않음

- GetKeyForItem
 * 바탕 객체로부터 항목의 키를 계산해서 돌려줌
 * 커스텀 컬렉션 클래스 작성자는 이 추상 메서드를 반드시 구현해야 함

- ChangeItemKey
 * 항목의 키를 변경하는데 쓰임(내부 사전 갱신을 위해)


- Dictionary
 * 조회를 구현하는 데 쓰이는 내부 사전을 돌려줌

- 컬렉션 생성시 생성 문턱값(Creation Threshold) 설정 가능
 * 요소 개수가 해당 문턱값에 도달해야 내부 사전이 생성됨
 * 그 이전에는 키 기반 조회를 선형 검색으로 해결함

- Collection<T>에서 다뤘던 예제의 KeyedCollection<TKey,TItem> 버전

namespace Practice
{
    class Program
    {
        public class Animal
        {
            string name;
            public string Name
            {
                get { return name; }
                set
                {
                    if(Zoo != null)
                    {
                        Zoo.Animals.NotifyNameChange(this, value);                        
                    }
                    name = value;
                }
            }
            public int popularity;
 
            public Zoo Zoo { get; internal set; }
 
            public Animal(string name, int popularity)
            {
                this.Name = name;
                this.popularity = popularity;
            }
        }
 
        public class AnimalColletion : KeyedCollection<string, Animal>
        {
            Zoo zoo;
 
            public AnimalColletion(Zoo zoo)
            {
                this.zoo = zoo;
            }
 
            internal void NotifyNameChange(Animal a, string newName)
            {
                this.ChangeItemKey(a, newName);
            }
 
            protected override string GetKeyForItem(Animal item)
            {
                return item.Name;
            }
 
            protected override void InsertItem(int index, Animal item)
            {
                base.InsertItem(index, item);
                item.Zoo = zoo;
            }
 
            protected override void SetItem(int index, Animal item)
            {
                base.SetItem(index, item);
                item.Zoo = zoo;
            }
 
            protected override void RemoveItem(int index)
            {                
                this[index].Zoo = null;
                base.RemoveItem(index);
            }
 
            protected override void ClearItems()
            {
                foreach (var item in this)
                {
                    item.Zoo = null;
                }
                base.ClearItems();
            }
        }
 
        // AnimalCollection을 노출하는 클래스
        public class Zoo
        {            
            public readonly AnimalColletion Animals;
            public Zoo()
            {
                Animals = new AnimalColletion(this);
            }
        }
 
        static void Main(string[] args)
        {
            Zoo zoo = new Zoo();
            zoo.Animals.Add(new Animal("개", 20));
            zoo.Animals.Add(new Animal("고양이", 50));
 
            foreach (var item in zoo.Animals)
            {
                Console.WriteLine($"{item.Name} : {item.popularity}");
            }
 
            Console.WriteLine(zoo.Animals[0].Name);
            Console.WriteLine(zoo.Animals["개"].popularity);
 
            zoo.Animals["고양이"].Name = "닭";
 
            Console.WriteLine(zoo.Animals["닭"].popularity);
        }
    }
}

DictionaryBase 클래스

 

DictionaryBase 클래스 (System.Collections)

강력한 형식의 키/값 쌍 컬렉션에 대한 abstract 기본 클래스를 제공합니다.

learn.microsoft.com

- KeyedCollection의 비제네릭 버전
- IDictionary를 구현

 * 클래스 파생 없이도 키를 얻을 수 있지만 기반 클래스 용도로는 적절하지 못하다


- CollectionBase같은 On~ 후킹 메서드 사용
- 이건 하위 호환으로 남겨두고, 그냥 KeyedCollection 씁시다.

 

 

반응형