[제네릭 2편] 제네릭 컬렉션의 사용법 2007-07-01
.NET/C# 2007. 7. 1. 23:33 |제네릭 컬렉션의 사용법
제네릭 컬렉션(=제네릭의 기능을 사용하고 있는 컬렉션 클래스)의 사용법은 어려운 것이 없다.
기본적인 사용법만 얘기한다면, 다음의 원칙을 기억하면 좋다.
★ 제네릭 컬렉션의 형명의 뒤에 취급하는 데이터의 형명을 < > 로 둘러싸서 기술한다( 복수의 데이터형을 취급하는 경우에는 콤마로 구분한다)
즉, 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); 가 실행된다.
'.NET > C#' 카테고리의 다른 글
[제네릭 4편] 제약이 붙은 제네릭 클래스 (0) | 2007.07.04 |
---|---|
[제네릭 3편] Hashtable 클래스와 Dictionary 클래스의 비호환성 (0) | 2007.07.02 |
[제네릭 2편] 제네릭 컬렉션의 사용법 2007-07-01 (0) | 2007.07.01 |
[제네릭 1편] 제네릭이란 무엇인가? 새로운 컬렉션의 소개 (0) | 2007.06.30 |
참조된 어셈블리의 Reflection 트러블 슈팅 (0) | 2007.06.02 |
Reflection 을 통한 Event 제어 (0) | 2007.05.17 |
댓글을 달아 주세요