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 으로 옮겨지게 된다.
 
원문 - http://www.atmarkit.co.jp 
Posted by 땡초 POWERUMC
TAG ,

댓글을 달아 주세요