제약이 붙은 제네릭 클래스
어떠한 형태로도 받아 들이는 제네릭은 강력하다. 이러한 제네릭의 <T> 는 대부분의 클래스가 될 수 있다.
 
그 강력함을 제약해 사용하기 쉽게 하는 수단이 있다. 구체적으로는, 형 파라메터에 제약 리스트를 붙일 수 있다는 것이다. 제약 리스트에는, 클래스나 인터페이스, 생성자의 제약을 지정할 수 있다.
 
이용 빈도는 낮다고 생각되기 때문에 자세한 설명을 하지 않는다.
아래의 샘플을 보자. 리스트12는 인터페이스 제약, 생성자 제약을 붙였다.
 
using System;
 
// IDisposable 인터페이스를실장한다Sample 클래스
public class Sample : IDisposable
{
 public void Dispose()
 {
    Console.WriteLine("Disposed");
 }
}
 
// 제약이붙었다MyClass<T> 클래스
public class MyClass<T> where T : IDisposable, new()
{
 public T Value = new T();
 
 public void Close()
 {
    Value.Dispose();
 }
}
 
class Program
{
 static void Main(string[] args)
 {
    MyClass<Sample> sample = new MyClass<Sample>();
    sample.Close(); // 출력:Disposed
 }
}
리스트12 제약이 붙은 제네릭 클래스의 예
 
여기에 제약을 붙이고 있는 것은,
 
where T : IDisposable, new()
 
부분이다. Where 는 클래스의 제약을 나타내고, T 에 대한 제약을 의미한다. 그 이후 쓰여진 인터페이스명의 IDisposable 은 형 파라메터 T 에 IDisposable 인터페이스를 구현한 형태에만 지정할 수 있다. 추가로 콤마로 구분하여 new() 는 형 파라메터에 T의 형에 인수가 없는 생성자로 제약한다.
 
이 제약은, 단지 사용법에 제한을 두는 것은 아니다.
MyClass<T> 클래스 내에서,
 
Value.Dispose();
 
처럼 사용한다.(IDisposable 인터페이스의) Dispose 메서드를 호출할 수 있는 것은, 반드시 IDisposable 인터페이스를 구현해야 한다고 하는 제약이 있기 때문이다. IDisposable 인터페이스의 제약이 없으면, T 에 Dispose 메서드가 존재하는지 아닌지는 예측 불가능하고, 컴파일 에러가 된다.
 
,
 
public T Value = new T();
 
를 사용할 수 있는 것은, 생성자 제약에 의해 T 에는 인수가 없는 생성자가 존재하면 사용할 수 있도록 제약했기 때문이다. 만약 그러한 제약이 없으면 T 에 인수가 없는 생성자가 있는지 아닌지는 예측하지 못하고, 컴파일 에러가 난다.
 
Posted by 땡초 POWERUMC
TAG c#, generic

댓글을 달아 주세요

HashTable 클래스와 Dictionary 클래스의 비호환성
제네릭 컬렉션은 용이하게 사용할 수 있어 당장이라도 사용하고 싶을 것이다.
 
그러나, 기존 C# 1.x 버전에서 제네릭 버전으로 치환하면서 대체할 수 없는 경우도 있다. 특히 심각한 것은, HashTable 클래스와 Dictionary 클래스의 비호환 동작의 경우다. 실제로 다음과 코드를 보자.
 
using System;
using System.Collections;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    try
    {
      // Hashtable 클래스(종래의콜렉션·클래스)
      Hashtable hashtable = new Hashtable();
      Console.WriteLine(hashtable[0] == null);
 
      // Dictionary 클래스(제네릭·콜렉션)
      Dictionary<int, object> dictionary
                                 = new Dictionary<int, object>();
      Console.WriteLine(dictionary[0] == null); // 예외가발생
    }
    catch (Exception e)
    {
      Console.WriteLine(e.ToString());
    }
 }
}
리스트9 Hashtable 클래스와 Dictionary 클래스의 비호환 동작 예
 
True
System.Collections.Generic.KeyNotFoundException: 지정된키는디렉토리내
에존재하지않았습니다.
   장소System.ThrowHelper.ThrowKeyNotFoundException()
   장소System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   장소Program.Main(String[] args) 장소……\Program.cs: 15
 
리스트9 실행 결과
 
존재하지 않는 키에 대한 엑세스는, 기존의 Hashtable 클래스에서는 null을 돌려주지만, Dictionary 클래스에서는 System.Collections.Generic.KeyNotFoundException 예외를 던진다.
 
어느 키에 대한 값이 존재 하는지 아닌지를 판정하는 처리는 생각보다는 자주 있지만, 만약 위와 같은 샘플 코드의 방법으로 판정하고 있는 경우는 고쳐 쓸 필요가 있다.
 
어느키가 컬렉션에 포함되어 있을까는, ContainsKey 메서드로 판정할 수 있다. 또, 키의 존재 체크와 값을 취득을 1회 실시하는 경우는, TryGetValue 메서드라고 하는 편리한 메서드도 있다.
 
If( Dictionary 오브젝트.TryGetValue(키, out value)) { xxxx }
 
그렇다고 키가 존재하는 경우에게만 변수 value에 값이 들어와, if 문장의 조건이 성립하므로, xxxx 부분에 value 의 값을 사용하는 코드를 안전하게 쓸 수 있다.
 
그러나 왜 이러한 사양 변경을 한 것일까. 잘 생각하면 알것이다.
Dictionary 클래스의 사양은 너무나도 합리적이다.
 
예를 들면, C# 2.0 의 신기능, nullable 을 사용하면, 값으로 null도 대입할 수 있다. int 형에 null 을 넣어 용이하게 사용할 수 있다. 이러한 형태의 값을 컬렉션에 추가했을 때, 당연히 null 도 유효한 값이라고 보여지지 않으면 안된다.
 
이것은 다음의 코드를 보자( int? 는 nullable 형의 int 형태 )
using System;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    Dictionary<int, int?> dictionary = new Dictionary<int, int?>();
    dictionary[0] = null;
 
    Console.WriteLine(dictionary.Count); // 출력:
    Console.WriteLine(dictionary[0] == null); // 출력:True
 }
}
리스트10 null 이지만 값이 되는 컬렉션
 
이 프로그램은 컬렉션에 1개의 키와 값을 넣고 있다. 또한 dictionary.Count 의 값이 1 인것을 확인할 수 있다. 그러나 값을 꺼내보면 null 이다.
 
만약, 키가 존재하지 않을 때에 얻을 수 있는 값도 null 이면, 들어있는 값이 null일 때, 키가 존재하는지 그렇지 않은지 구별할 수 없게 된다. 키가 없을 때는 예외를 던지는 것은, 구별을 명확하게 하기 위해 필요하게 되는 대처가 되는 것이다.
 
하지만, 예외는 무거운 처리이므로, 예외를 사용하는 것보다 ContainsKey 메서드로 알아보는 것이 좋을 것이다.
 
제네릭 클래스를 만들자
제네릭 클래스를 만드는 예를 보자. 여기에서는, 임의의 타입을 변수에 저장하는 클래스를 작성해 보자.
using System;
 
// 제네릭인MyClass 클래스의정의( T ()는형태파라미터의이름)
public class MyClass<T>
{
 public T Value;
 
 public MyClass(T v)
 {
    Value = v;
 }
}
 
class Program
{
 static void Main(string[] args)
 {
    MyClass<int> i = new MyClass<int>(0);
    Console.WriteLine(++i.Value); // 출력:
 
    MyClass<string> s = new MyClass<string>("abc");
    Console.WriteLine(s.Value.ToUpper()); // 출력:ABC
 }
}
리스트11 제네릭 클래스
 
먼저 클래스의 이름 뒤에 < > 로 둘러싸 형 파라메터의 이름을 쓴다. 여기에 쓰는 이름은 실제로 존재하지 않는 형태의 이름이 좋다. 사용할 땐, 이 이름이 실제로 지정된 형으로 변환되기 때문이다. 덧붙여, 여기에서는 <T> 또는 T로 시작되는 이름 <TValue>를 사용하는 것이 일반적이다.
 
그리고, 선언된 형 파라메터의 이름을 마치 실제로 존재하는 형의 이름인 것 처럼 사용해 클래스를 구현한다.
 
예를 들면 public T Value; 라고 하는 변수 선언은, 마치 T 가 형이 있는 것 같이 기술하고 있지만, 실제로 사용되는 경우에는 <T> 로 지정된 int 나 string 으로 옮겨지게 된다.
 
Posted by 땡초 POWERUMC
TAG c#, generic

댓글을 달아 주세요

제네릭 컬렉션의 사용법
 
제네릭 컬렉션(=제네릭의 기능을 사용하고 있는 컬렉션 클래스)의 사용법은 어려운 것이 없다.
 
기본적인 사용법만 얘기한다면, 다음의 원칙을 기억하면 좋다.
 
★ 제네릭 컬렉션의 형명의 뒤에 취급하는 데이터의 형명을 < > 로 둘러싸서 기술한다( 복수의 데이터형을 취급하는 경우에는 콤마로 구분한다)
 
즉, List<string>
 
과같이 사용하면 된다. new ArrayList() 와 같이 사용하듯, new List<string>() 과 같이 사용하면 된다.
 
Dictionary 클래스와 같이, 키(key)와 값(value)의 2개의 형태를 이용하는 클래스의 경우는, < > 안에 형태를 2개 지정한다.키가 정수이고 값이 문자열이라면,
 
Dictionary<int, string>
 
과 같이 사용한다. 그리고, 기존의 컬렉션과 취급방식은 큰 차이가 없다.
다음은 이러한 클래스의 사용예이다.
 
using System;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    // 문자열을요소로서취급한다List 클래스
    List<string> list = new List<string>();
    list.Add("Sample List");
 
    Console.WriteLine(list[0]); // 출력:Sample List
 
    // 키가정수로값이문자열의요소를취급한다Dictionary 클래스
    Dictionary<int, string> dic = new Dictionary<int, string>();
    dic[0] = "Sample Dictionary";
 
    Console.WriteLine(dic[0]); // 출력:Sample Dictionary
 }
}
리스트4 List 클래스와 Dictionary 클래스의 사용예
 
물론, < > 로 둘러싸 지정한 형은, 그것을 상속한 형 또한 받아 들인다. 인터페이스를 지정했을 경우는, 그 인터페이스를 상속한 형 또한 받아 들인다.
 
using System;
using System.Collections.Generic;
 
// 인터페이스의정의
public interface ISample
{
 void SayISample();
}
 
// 추상클래스의정의
public abstract class SampleBase
{
 public abstract void SaySample();
}
 
// SampleBase 클래스를계승해,ISample 인터페이스를실장한클래스
public class Sample : SampleBase, ISample
{
 public override void SaySample()
 {
    Console.WriteLine("Sample!");
 }
 public void SayISample()
 {
    Console.WriteLine("ISample!");
 }
}
 
class Program
{
 static void Main(string[] args)
 {
    // SampleBase 형태의콜렉션에Sample 오브젝트를추가
    List<SampleBase> list1 = new List<SampleBase>();
    list1.Add(new Sample());
 
    list1[0].SaySample(); // 출력:Sample!
 
    // ISample 형태의콜렉션에Sample 오브젝트를추가
    List<ISample> list2 = new List<ISample>();
    list2.Add(new Sample());
 
    list2[0].SayISample(); // 출력:ISample!
 }
}
 
리스트5 상속과 인터페이스를 사용한 예
 
제네릭 메서드와 형 추론
 
제네릭은 클래스 이외에도 사용할 수 있다. 구조체나 인터페이스에도 사용할 뿐만 아니라, Delegate 나 메서드에도 사용할 수 있다.
 
여기에서는, 특히 제네릭을 사용한 메서드인 [제네릭 메서드]에 주목하자. 이것은, 바로 사용할 수 있는 편리한 기능이 있지만, 주의를 필요로 하는 부분도 있기 때문이다.
 
클래스 라이브러리가 제공하는 제네릭 메서드중에서, 특히 빈번히 사용한 수 있는, 배열을 소트하는 Array 클래스의 Sort 메서드일 것이다. 사용법은 클래스와 같이 메서드의 이름뒤에 < > 로 둘러싸 형명을 쓰면 된다.
 
using System;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    int[] array = { 3, 2, 1 };
    Array.Sort<int>(array); // 제네릭·메소드의호출
 
    foreach (int i in array) Console.WriteLine(i);
    // 출력:
    // 1
    // 2
    // 3
 }
}
리스트6 제네릭 메서드의 사용예
 
<int>를 지정한 Sort<int> 메서드는 인수의 int 형태의 배열을 받아, 그 요소를 정렬하기 위한 메서드다.
 
그러나, Array.Sort 의 뒤엔 <int> 가 있다. 여기엔 큰 의미가 없다. 왜냐하면, 인수로 지정한 배열 변수(array)의 형이 int[] 이기 때문에, 통상 <int> 를 기술하는 것은 의미가 없기 때문이다.
 
여기에서, [형 추론] 이라고 하는 기능을 사용해 C# 컴파일러에 형 추리를 시킬 수 있다. 이 기능이 사용하기 위해 <int> 를 생략해도 의도대로 동작한다.
 
using System;
 
class Program
{
 static void Main(string[] args)
 {
    int[] array = { 3, 2, 1 };
    Array.Sort(array); // <int> ()를생략하고있다
 
    foreach (int i in array) Console.WriteLine(i);
    // 출력:
    // 1
    // 2
    // 3
 }
}
리스트7 형 추론에 의해 제네릭 메서드를 사용한 예
 
이 코드는 언뜻, 기존 C# 1.x 의 Sort 메서드의 호출과 구별이 되지 않는다.
 
그러나, ildasm 이나 .NET Reflector 에 의해 생성된 코드를 보면, 제네릭이 아닌 기존의 Sort 메서드가 아니고, 형 추론을 해 Sort<int> 메서드가 호출되는 것을 알 수 있다.
 
그런데, 형 추론은 강력하기는 하지만, 보기 드물게 형 추론을 사용 할 수 없는 경우도 있다.
 
예를 들면, IComparable 인테페이스를 구현한 base class 와 상속 클래스가 있는 경우, 그것을 명시적으로 구분하여 사용하지 않으면 결과가 바뀌어 버리는 일이 있다. 아래의 예를 보자.
 
using System;
 
public class ClassA : IComparable<ClassA>
{
 public int Value;
 public ClassA(int v)
 {
    Value = v;
 }
 
 public int CompareTo(ClassA other)
 {
    return Value.CompareTo(other.Value);
 }
}
 
public class ClassB : ClassA, IComparable<ClassB>
{
 public ClassB(int v) : base (v) { }
 
 public int CompareTo(ClassB other)
 {
    return other.Value.CompareTo(Value);
 }
}
 
class Program
{
 static void Main(string[] args)
 {
    ClassB[] array = {
      new ClassB(3),
      new ClassB(2),
      new ClassB(1),
    };
 
    Array.Sort<ClassA>(array);
    foreach (ClassB b in array) Console.WriteLine(b.Value);
    // 출력:
    // 1
    // 2
    // 3
 
    Array.Sort<ClassB>(array);
   foreach (ClassB b in array) Console.WriteLine(b.Value);
    // 출력:
    // 3
    // 2
    // 1
 }
}
 
리스트8 형 추론을 사용할 수 없는 예
 
이 예제에 대해, 형 지정을 생략해 Array.Sort(array); 라고 기술하면, Array.Sort<ClassB>(array)라고 추론되지만, 그 실행 결과는 Array.Sort<ClassA>(Array); 가 실행된다.
 

Posted by 땡초 POWERUMC
TAG c#, generic

댓글을 달아 주세요


제네릭이란 무엇인가?
 
C# 1.x 프로그래밍의 불만이란 무엇일까.
 
구체적으로는 여러 가지가 있다고 생각하지만, 아마 아래의 내용에 대해서는, 대부분 C# 1.x 프로그래머가 불만이라고 느끼고 있을 것이다.
 
★ 컬렉션의 요소를 엑세스 할 때, 캐스트가 요구된다.
 
구체적으로 말하면, 다음과 같다.

using System;
using System.Collections;
 
class Program
{
 static void Main(string[] args)
 {
    ArrayList list = new ArrayList();
    list.Add("Hello!");
 
    Console.WriteLine(((string)list[0]).ToUpper()); // 캐스트필요
    // 출력:HELLO!
 }
}
리스트1 C# 1.x 에서 컬렉션을 사용한 예

 
이 프로그램에서는, 가변 사이즈 1차원 리스트 컬렉션을 구현했다.
ArrayList 클래스에서 문자열을 넣고 나서 꺼내고 있다. 여기서, 아무일도 없어 문자열을 컬렉션에 넣을 수 있는데, 꺼낼 때는
 
(string)list[0]
 
이러한 캐스트가 필요하게 된다. 그리고, 이 캐스트 작업은 귀찮다.
 
우선, 쓰지 않으면 안되는 문자가 증가해 귀찮다. 본질적으로 필요가 없는 문자가 소스 코드상에 증가하면, 그만큼 기독성도 나빠져, 점검성도 떨어진다. 하지만, 이것은 아직 시초에 지나지 않는다.
 
캐스트를 사용하면, 그만큼 실행 속도가 저하된다. 캐스트는 무거운 처리인 것이다. 덧붙여서, C# 의 캐스트보다 as 연산자를 사용하는 것이 더 빠르지만, as 연산자는 캐스팅을 할 수 없을 때 null 이 리턴되고, 이 null 을 비교해야 하기 때문에 취급하기 어렵다.
 
그리고, 캐스트를 임용하면, 형변환의 타당성 체크를 컴파일시에 실시할 수 없게 되어, 성능이 저하된다. 이것을 [객체의 형변환은 실행시에 체크되기 때문에 문제 없다] 라고 주장하는 사람도 있지만, 본질적으로 이것은 무엇이 문제일까를 잃은 발언이라고 할 수 있다. 형변환이 런타임시 체크되는 것은, 체크가 지연된다는 것이 문제가 아니고, 형변환 체크가 [개별의 값]에 대해서 행해진다고 하는 문제인 것이다.
 
보다 구체적으로 보자. 캐스트를 임용하면, 우선 이하와 같은 상황이 발생한다.
 
★ 테스트를 할 때, 모든 행이 실행된다고는 할 수 없다. 즉, 실행되지 않은 행에 쓰여진 캐스트의 타당성은 체크되지 않는다.
 
이것은 테스트 지원 툴등을 이용하여, 커버리지(Coverage)가 100%를 목표로 하면 해소할 수 있을 지도 모른다.
 
★ 우연히 테스트 실행시에 주어진 값에 대해서 캐스트가 타당하다고 확인하지만, 실행시에 모든값에 대해서, 캐스트가 타당한가는 체크되지 않는다.
 
이러한 문제는, 작은 프로그램에서도 버그가 생길 수도 있고, 큰 규모의 프로그램에서는 상당한 리스크가 된다. 예를 들면, 컬렉션에 생각할 수 도 없는 형태의 값이 들어가 있고, 잘 되어야할 캐스트가 실패한다라고 가정하에, 사양 변경이 많은 프로젝트나, 의사소통이 불충분한 개발 프로젝트에서는 큰 충격이 될 수 밖에 없다.
 
이러한 문제는, 가능한 여러 형타입을 컴파일시에 실시하는것으로 해결할 수 있다.
 
이러한 작업이 큰 불만이 아닐 수도 있다. 그러나, C++ 의 template 이라 불리는 기능은, 컬렉션이나 클래스의 캐스트 지옥에서 벗어날 수 있었다. 때문에 C# 1.x 로서는 큰 불만이 될 수 있을거라 생각한다.
 
그리고, C# 2.0 은 이러한 문제를 해소할 수 있는 기능이 추가 되었다. 그것은 C++ 의 template 기능과 다른, 제네릭이라고 하는 비슷하면서도 다른 기능이다.
 
이 기능을 사용하면, 리스트1 예제로부터 캐스트를 추방할 수 있는 것을 알 수 있다.

using System;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    List<string> list = new List<string>();
    list.Add("Hello!");
 
    Console.WriteLine(list[0].ToUpper()); // 캐스트불요
    // 출력:HELLO!
 }
}
리스트2 C# 의 제네릭을 사용한 예

 
여기서 list 변수를 선언할 때,
 
List<string>
 
과 같이, string 이라는 타입을 명시하는 것으로써, 소스코드에서 캐스트를 사용하지 않는다.
 
새로운 컬렉션의 소개
 
리스트1에서 사용하고 있는 List 클래스는, 기존의 ArrayList 에 상응하는 컬렉션 클래스다. 덧붙여서 List 클래스가 속하는 네임스페이스는 System.Collections 가 아니고 System.Collections.Generic 이다.
 
비쥬얼스튜디오 2005에서는 코드파일을 만들게 되면, 기본적으로
 
using System.Collections.Generic;
 
위와 같이 작성된다.
 
종래의 컬렉션
제네릭의 컬렉션
기능
ArrayList
List
가변 사이즈의1 차원 리스트
Hashtable
Dictionary
키/치 페어의 컬렉션
Queue
Queue
선입처 내밀기 컬렉션
SortedList
SortedList
키로 소트 된 키/치 페어의 컬렉션
Stack
Stack
후입선출 컬렉션
컬렉션·클래스의 대응표
제네릭의 컬렉션·클래스는.NET Framework 2.0 그리고 클래스·라이브러리에 추가된 것.물론 이것에는 종래의 컬렉션·클래스도 포함되어 있다.
 
덧붙여서, Stack 클래스와 같이 기존의 이름과 같은 제네릭 클래스가 있는것에 주의가 필요하다. 이것은 다른 네임스페이스에 속하는 다른 클래스다. 그중, 특히 큰 문제는 다음에 설명한다.
 
그런데, ArrayList 로부터 List 로의 이름 변경은, 자주 사용되는 클래스의 이름이 짧아졌다고 하는 것으로 사용하기 편해졌다고 말할 수 있을 것이다.( ArrayList 와 상응하는 List<string> 처럼, 짧아졌다고는 말하기 어렵지만…)
 
한편, HashTable 은 Dictionary 로의 이름변화는, 기존 이름과 전혀 다른 이름변화라고 할 수 있다. 이름으로 유추할 수 없고, 새로운 클래스명을 암기 할 수 밖에 없을 것이다.
 
새로운 컬렉션 클래스 – LinkedList 클래스
 
LinkedList 클래스는 1차원적인 배열만 취급한다고 하는 의미에서 ArrayList/List 클래스와 닮을 기능을 가지고 있지만, 다른 성질을 가지고 있다.
 
주된 차이점은 두가지 이다. 첫째, 인덱스에 의한 엑세스를 할 수 없다. 즉, 인덱서에 번호를 지정하여 엑세스 할 수 없는 것이다. 둘째, 리스트 중간에 다른 요소를 삽입/삭제를 빠르게 할 수 있다.
 
여기서, List 클래스와 LinkedList 클래스의 삽입 성능의 차이를 보자. 아래는 2개의 요소를 가지는 리스트의 중간에 1만개의 요소를 삽입하는 작업을 100회 반복하는 샘플 코드이다.
 

using System;
using System.Collections.Generic;
 
class Program
{
 static void Main(string[] args)
 {
    // List 클래스의이용
    DateTime start1 = DateTime.Now;
    for (int i = 0; i < 100; i++)
    {
      List<string> list1 = new List<string>();
      list1.Add("First");
      list1.Add("Last");
      for (int j = 0; j < 10000; j++)
      {
        list1.Insert(1, "Inserted");
      }
    }
    DateTime end1 = DateTime.Now;
    Console.WriteLine(end1 - start1);
 
    // LinkedList 클래스의이용
    DateTime start2 = DateTime.Now;
    for (int i = 0; i < 100; i++)
    {
      LinkedList<string> list2 = new LinkedList<string>();
      list2.AddFirst("First");
      list2.AddLast("Last");
      for (int j = 0; j < 10000; j++)
      {
        list2.AddAfter(list2.First, "Inserted");
      }
    }
    DateTime end2 = DateTime.Now;
    Console.WriteLine(end2 - start2);
 }
}
리스트3 List와 LinkedList 의 삽입 성능의 차이
Pentium D 3.2Ghz CPU 로 디버그 빌등에 의한 실행 결과

 

00:00:05.3480694 (List 클래스이용시)
00:00:00.0820164 (LinkedList 클래스이용시)
리스트3의 실행결과

 
위의 결과를 통해, 엄청난 성능 차이가 나고 있다.
 
LinkedListNode 클래스의 인스턴스는 리스트에 포함하거나 제외하거나 하는 작업에 용이하고, 그 특징을 잘 활용하면 리스트를 분해해 재구축하는 처리에 좋은 성능을 낸다. ArrayList/List 클래스를 사용하고 성능이 나오지 않는 경우는 LinkedList 클래스로 테스트해 보면 좋을 것이다.
 
다만, 메모리의 사용량(확보되는 오브젝트 수)는 LinkedList 클래스가 더 커진다고 하는 디메리트도 있다. 위의 프로그램을 따로따로 분리해 실행시키면, 프로그램 종료시점으로 List 클래스는 약 6Mbytes 정도의 메모리를 소비하고 있는데 비해, LinkedList 클래스는 보다 많은 약 11Mbytes 정도의 메모리를 소비하고 있다.
 
새로운 컬렉션 클래스 – SortedDictionary 클래스
 
C# 2.0 에서 추가된 SortedDictionary 클래스는 SortedList 클래스와 기능이 비슷하다. 그러나 완전히 같지는 않다. SortedDictionary 클래스와 SortedList 클래스의 관계는, LinkedList 클래스와 List 클래스의 관계와 비슷하다.
 
SortedDictionary 클래스와 SortedList 클래스의 차이는, 메모리의 사용 방법과 삽입 및 삭제의 속도이다. 주된 차이점은 다음과 같다.
 
SortedList 클래스는 SortedDictionary 클래스만큼 메모리를 사용하지 않는다.
 
SortedDictionary 클래스에는 정렬되지 않은 데이터에 대해서 고속 삽입 및 삭제가 가능
 
모든 리스트의 데이터를 한번에 꺼내는 경우 SortedList 클래스가 SortedDictionary 클래스보다 빠른 성능을 보인다.
 
Posted by 땡초 POWERUMC
TAG c#, generic

댓글을 달아 주세요