본문 바로가기
C#

[C#] Collection<T>, CollectionBase, ReadOnlyCollection<T>

by DANEW 2023. 9. 28.

커스텀화 가능한 컬렉션과 프록시
- 일반적인 컬렉션 클래스들은 바로 인스턴스화 해서 쓸 수 있지만, 세밀한 컨트롤이 불가능함

- 예를 들자면...
 * 항목이 추가되거나 제거되면 이벤트를 발동
 * 추가 또는 제거된 항목에 맞게 속성들을 갱신
 * 규칙에 맞지 않는 연산을 검출하여 예외를 던짐

- System.Collections.ObjectModel 이름 공간 안에는 이런 용도를 위한 클래스들을 제공
 * IList<T>나 IDictionary<TKey,TValue>를 구현하는 래퍼(Wrapper)나 프록시(Proxy)
 * 메서드들을 바탕 컬렉션에 전달하는 역할
 * Add, Remove 같은 연산을 일종의 관문(Gateway) 역할을 하는 가상 메서드에 연결
 * 커스텀 컬렉션을 만들 때에는 그 가상 메서드들을 적절히 재정의함으로서 원하는 기능성을 구현
  ex) System.Windows.Form 클래스의 Public 컨트롤들의 컬렉션

 

Collection<T> 클래스

 

Collection<T> 클래스 (System.Collections.ObjectModel)

제네릭 컬렉션에 대한 기본 클래스를 제공합니다.

learn.microsoft.com

namespace System.Collections.ObjectModel
{
    public class Collection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        public Collection();
        public Collection(IList<T> list);
 
        public T this[int index] { get; set; }
 
        public int Count { get; }
        protected IList<T> Items { get; }
 
        public void Add(T item);
        public void Clear();
        public bool Contains(T item);
        public void CopyTo(T[] array, int index);
        public IEnumerator<T> GetEnumerator();
        public int IndexOf(T item);
        public void Insert(int index, T item);
        public bool Remove(T item);
        public void RemoveAt(int index);
        protected virtual void ClearItems();
        protected virtual void InsertItem(int index, T item);
        protected virtual void RemoveItem(int index);
        protected virtual void SetItem(int index, T item);
    }
}

- List<T>에 대한 커스텀화 가능 래퍼
- IList<T>와 IList를 구현, 몇개의 가상 메서드들과 보호된 속성도 정의
- 가상 메서드들은 일종의 관문 역할을 수행
- 커스텀 컬렉션 구현자는 이들을 후킹 지점으로 사용해서 기본 행동 방식을 변경하거나 개선
- IList<T>를 받는 생성자의 경우 주어진 목록을 복사하는 대신, 그냥 자신을 해당 목록의 프록시(Proxy)로 삼음
 * 목록에 생긴 변화는 해당 Collection<T>에 반영되며, Collection<T>에 가해진 변형은 해당 목록에도 가해짐
 * 단, Collection<T>의 가상 메서드의 호출은 일어나지 않음

반응형

 

CollectionBase 클래스

 

CollectionBase 클래스 (System.Collections)

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

learn.microsoft.com

- Collection<T>의 비제네릭 버전
- 구현해야 할 메서드가 제네릭 버전의 2배
- OnInsert, OnInsertComplete ↔ InsertItem
- OnSet, OnSetComplete ↔ SetItem
- OnRemove, OnRemoveComplete ↔ RemoveItem
- OnClear, OnClearComplete ↔ ClearItem
- 이 클래스를 상속할 때는 형식 있는 메서드도 구현해야 함 (적어도 형식 있는 인덱서와 Add 메서드)

 

 

ReadOnlyCollection<T> 클래스

 

ReadOnlyCollection<T> 클래스 (System.Collections.ObjectModel)

제네릭 읽기 전용 컬렉션의 기본 클래스를 제공합니다.

learn.microsoft.com

namespace System.Collections.ObjectModel
{
    public class ReadOnlyCollection<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        public ReadOnlyCollection(IList<T> list);
       
        public T this[int index] { get; }
       
        public int Count { get; }
        protected IList<T> Items { get; }
       
        public bool Contains(T value);
        public void CopyTo(T[] array, int index);
        public IEnumerator<T> GetEnumerator();
        public int IndexOf(T value);
    }
}

- 기존 컬렉션의 읽기 전용 시각을 제공하는 하나의 래퍼 또는 프록시
- 소비자에게는 읽기 전용 접근만 제공하되, 클래스 자체는 여전히 컬렉션을 내부적으로 수정 할 수 있는 클래스를 만들 때 사용
- 입력 컬렉션을 인수로 받되, 복사본을 사용하는 것이 아니므로 입력된 컬렉션이 변하면 읽기 전용 컬렉션에도 그 변경이 반영됨

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
 
namespace Practice
{
    class Program
    {
        public static void Main(string[] arg)
        {
            RocTest rocTest = new RocTest();
 
            Console.WriteLine(rocTest.Names.Count);
            rocTest.AddInternally("test");
            Console.WriteLine(rocTest.Names.Count);
 
            // 여기서 예외 발생
            ((IList<string>)rocTest.Names).Add("test");
        }
    }
   
    public class RocTest
    {
        List<string> names;
        public ReadOnlyCollection<string> Names { get; /*private set;*/ }
 
        public RocTest()
        {
            names = new List<string>();
            Names = new ReadOnlyCollection<string>(names);
        }
 
        public void AddInternally(string text)
        {
            names.Add(text);
        }
    }
}

 

 

 

반응형