06a 장 . 컬렉션

23
C# 06a 장 . 장장장

Upload: hogan

Post on 24-Feb-2016

75 views

Category:

Documents


0 download

DESCRIPTION

C#. 06a 장 . 컬렉션. 컬렉션 : 변수들의 조직적인 집합 컬렉션 클래스 : 컬렉션 집합을 저장하고 관리하는 클래스 컬렉션의 종류 : 배열 , 배열 리스트 , 해시 테이블 , 큐 , 스택 컬렉션의 네임 스페이스 : System.Collections. 비제네릭 컬렉션 ; System.Collections 제네릭 컬렉션 : System.Collections.Generic. 제너릭. 제너릭 (Generic) - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 06a 장 .  컬렉션

C#06a 장 . 컬렉션

Page 2: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

2 / 38

컬렉션 : 변수들의 조직적인 집합

컬렉션 클래스 : 컬렉션 집합을 저장하고 관리하는 클래스

컬렉션의 종류 : 배열 , 배열 리스트 , 해시 테이블 , 큐 , 스택

컬렉션의 네임 스페이스 : System.Collections

비제네릭 컬렉션 ; System.Collections

제네릭 컬렉션 : System.Collections.Generic

Page 3: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

3 / 38

제너릭

제너릭 (Generic) : 타입 인수를 사용하여 일반화된 클래스나 메서드를 정의하는 기법

C# 2.0 부터 지원

C++ 의 템플릿과 유사

Page 4: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

4 / 38

제너릭이 필요한 이유1. using System;

2. class WrapperInt3. {4. int Value;5. public WrapperInt() { Value = 0; }6. public WrapperInt(int aValue) { Value = aValue; }7. public int Data8. {9. get { return Value; }10. set { Value = value; }11. }12. public void OutValue()13. {14. Console.WriteLine(Value);15. }16.}16.class WrapperString17.{18. string Value;19. public WrapperString() { Value = null; }20. public WrapperString(string aValue) { Value = aValue; }21. public string Data22. {23. get { return Value; }24. set { Value = value; }25. }26. public void OutValue()27. {28. Console.WriteLine(Value);29. }30.}

31.class CSTest32.{33. static void Main()34. {35. WrapperInt gi = new WrapperInt(1234);36. gi.OutValue();37. WrapperString gs = new WrapperString(" 문자열 ");38. gs.OutValue();39. }40.}

1. int 형을 정의한 클래스

2. string 형을 정의한 클래스

2 개의 클래스 (WrapperInt, Wrapper-String) 가 모두 내부 코드는 동일하다 . 제네릭으로 간단히 정의 가능

Page 5: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

5 / 38

제너릭이 필요한 이유using System;

class Wrapper<T>{ T Value; public Wrapper() { Value = default(T); } public Wrapper(T aValue) { Value = aValue; } public T Data { get { return Value; } set { Value = value; } } public void OutValue() { Console.WriteLine(Value); }}

class CSTest{ static void Main() { Wrapper<int> gi = new Wrapper<int>(1234); gi.OutValue(); Wrapper<string> gs = new Wrapper<string>(" 문자열 "); gs.OutValue(); }}

1. 1 개의 클래스 (Wrapper<T>) 로 정의

2. 선언문의 <T> 가 타입 인수 (Type Parameter) 임

3. T 는 실제 타입을 위한 자료 표시이며 실제 타입은 객체를 생성할 때 지정된다 .

4. 타입 인수는 모든 곳에 사용 가능함( 필드 , 프로퍼티의 타입 , 메서드의 리턴값 , 메서드의 인수 타입 등 )

5. 예제에서는 3 군데에서 사용됨- Value 필드- 생성자의 인수 aValue- Data 프로퍼티의 타입

Page 6: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

6 / 38

• T 의 실제 타입 지정 하는 곳 : 객체 생성문의 <> 괄호 안에 지정• 앞의 예에서 Wrapper<int>, Wrapper<string> 으로 정의하였

다 . 이와 같이 Wrapper<double>, Wrapper<double> 같이 많은 클래스를 정의할 수 있다 .

• 제네릭은 클래스를 찍어내는 형틀이다 .

class Wrapper<T>{ T Value; public Wrapper() { Value = default(T); } public Wrapper(T aValue) { Value = aValue; } public T Data

class Wrapper<int>{ int Value; public Wrapper() { Value =0; } public Wrapper(string aValue) { Value = aValue; } public int Data

class Wrapper<string>{ string Value; public Wrapper() { Value =null; } public Wrapper(string aValue) { Value = aValue; } public string Data

class Wrapper<double>{ double Value; public Wrapper() { Value =0.0; } public Wrapper(double aValue) { Value = aValue; } public double Data

int

string

double

Page 7: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

7 / 38

• 개방형 타입 : 아직 타입이 결정되지 않은 Wrapper<T>• 폐쇄형 타입 : 타입이 결정된 Wrapper<int>• 개방형 타입은 클래스를 만드는 도구일 뿐 실제 클레스는 아니므로

객체를 생성하지 못한다 .

• 제네릭 타입 구체화 (Generic Type Instantiation): 개방형 타입의 타입 인수를 지정하여 폐쇄형 타입인 클래스를 생성하는 것

• T 가 값 타입일 경우 : 컴파일러가 각 타입별로 구체화 한다 .• T 가 참조 타입일 경우 : 하나의 클래스만 생성되고 모든 참조

타입에 대해 생성된 클래스를 재사용한다 .

• default 키워드 : T 의 기본값을 정의함• 제네릭에서는 T 가 어떤 타입이 될지 미리 알 수 없으므로

Value=0,Value=null 식으로 상수를 대입할 수 없다 .• default(T) 라는 표현식으로 T 에 따른 기본값을 표현한다 .

Page 8: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

8 / 38

using System;class CSTest{ static void Swap(ref int a, ref int b) { int t; t = a; a = b; b = t; } static void Swap(ref string a, ref

string b) { string t; t = a; a = b; b = t; } static void Main() { int i1 = 3, i2 = 4; Console.WriteLine("i1 = {0}, i2 = {1}",

i1, i2); Swap(ref i1, ref i2); Console.WriteLine("i1 = {0}, i2 = {1}",

i1, i2);

string s1 = " 멍멍 ", s2 = " 꼬꼬댁 "; Console.WriteLine("s1 = {0}, s2 = {1}",

s1, s2); Swap(ref s1, ref s2); Console.WriteLine("s1 = {0}, s2 = {1}",

s1, s2); }}

using System;

class CSTest{ static void Swap<T>(ref T a, ref T b) { T t; t = a; a = b; b = t; }

static void Main() { int i1 = 3, i2 = 4; Console.WriteLine("i1 = {0}, i2 = {1}", i1, i2); Swap(ref i1, ref i2); //Swap<int>(ref i1, ref i2); //<int> 생략 가능 Console.WriteLine("i1 = {0}, i2 = {1}", i1, i2);

string s1 = " 멍멍 ", s2 = " 꼬꼬댁 "; Console.WriteLine("s1 = {0}, s2 = {1}", s1, s2); Swap(ref s1, ref s2); //Swap<string>(ref s1, ref s2); Console.WriteLine("s1 = {0}, s2 = {1}", s1, s2); }}

제너릭을 이용한 2 개의 값 교환

Page 9: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

9 / 38

제약 조건• 제너릭 타입 인수 T 는 별다른 지정이 없으면 모든 타입을 적용할 수

있다 .• 제약 조건은 제네릭 선언문에 where 와 함께 지정하며 다음과

같은 종류가 있다 .

제약 조건 설명where T:struct T 는 값 타입이어야 하며 참조 타입을 쓸 수 없다 . 단 , Nullable

타입은 값 타입이지만 예외적으로 이 제약 조건에서 허용되지 않는다 .where T:class T 는 참조 타입이어야 하며 값 타입을 쓸 수 없다 .

where T:new() 디폴트 생성자가 있어야 한다 . new T() 형태로 객체를 생성할 수 있어야 한다 . 다른 조건과 함께 쓸 때는 제일 뒤에 지정해야 한다 .

where T:base T 는 base 로부터 파생된 클래스여야 한다 .

where T:Ibase T 는 Ibase 인터페이스를 반드시 구현해야 한다 . 클래스와는 달리 여러 개의 인터페이스를 지정할 수도 있다 .

where T:U 두 타입 인수 사이의 과계가 파생 관계여야 한다 . T 가 U 의 파생 클래스여야 한다 .

Page 10: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

10 / 38

값 타입만 가능한 예제 ( 제네릭 )using System;

class Wrapper<T> where T : struct{ T Value; public Wrapper() { Value = default(T); } public Wrapper(T aValue) { Value = aValue; } public T Data { get { return Value; } set { Value = value; } } public void OutValue() { Console.WriteLine(Value); }}

class CSTest{ static void Main() { Wrapper<int> gi = new Wrapper<int>(1234); gi.OutValue(); //Wrapper<string> gs = new Wrapper<string>(" 문자열"); //gs.OutValue(); }}

왼쪽의 마지막 2 줄을 실행 시켰을 때 메시지

Page 11: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

11 / 38

• 앞의 예는 Wrapper 제네릭 T 는 값 타입만 가능하다 .• Wrapper<int> 는 가능하지만 Wrapper<string>

이나 Wrapper<Human> 은 사용할 수 없다 .

• where T:class 로 바꾸면 T 는 참조 타입만 사용할 수 있고 값 타입은 사용할 수 없게 된다 .

• 제약 조건 중 가장 실용적인 것은 where T: base 형식이다 . 이 조건은 T 를 base 나 base 파생 클래스로 제한한다 .

Page 12: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

12 / 38

제약 조건 where T:baseusing System;class Human //1. Human 클래스 정의{ public virtual void Intro() { Console.WriteLine(" 나 사람 "); } }

class Student : Human // 2. Student 파생 클래스 정의{ public override void Intro() { Console.WriteLine(" 나 학생 "); } }

class CSTest //3. 제너릭 메서드 정의{ public static void OutValue<T>(T man) where T : Human { man.Intro(); } // 타입 인수 T 의 객체 man 을 인수로 받아 man.In-

tro 호출 // T 가 Human 의 후손이라는 제약 조건이 있기 때문에

호출 가능 static void Main() { Human A = new Human(); Student B = new Student(); string C = " 나 문자열 ";

OutValue(A); OutValue(B); //OutValue(C); }}

C 객체는 string 타입이며 Human 과는 관계가 없기 때문에 컴파일 되지 않는다 .string 클래스는 Intro 메서드를 가지고 있지 않기 때문에 이 타입의 객체로는 OutValue 가 동작하자 않아 컴파일 거부가 된다 .

Page 13: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

13 / 38

제약 조건 where T:base 제약 조건 제거 시using System;

class Human{ public virtual void Intro() { Console.WriteLine(" 나 사람 "); } }

class Student : Human{ public override void Intro() { Console.WriteLine(" 나 학생 "); } }

class CSTest{ public static void OutValue<T>(T man) { Human t = man as Human; if (t != null) { t.Intro(); } } static void Main() { Human A = new Human(); Student B = new Student(); string C = " 나 문자열 ";

OutValue(A); OutValue(B); OutValue(C); }}

1. 제약 조건 없이 제네릭으로 작성한 예제임

2. man 을 Human 으로 캐스팅하여 성공하면

호출하고 그렇지 않으면 아무런 동작도 하지

않는다

3. 그렇기 때문에 문자열 같은 잘못된 타입이

전달되어도 호출되었다가 그냥 리턴 함 .

제약 조건은 컴파일할 때 타입을 체크하여 불가능한

호출을 원천적으로 차단하고 캐스팅을 쵷소화

하는 역할을 한다 .

Page 14: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

14 / 38

제너릭 컬렉션

• 제네릭은 원래 C# 언어의 스펙에 포함되어 있던 기능이 아니다 .

• 제네릭은 문법을 복잡하게 만들고 컴파일을 느리게 만드는 주범인데다 컴파일러까지 복잡해져 비용이 많이 든다 .

• C# 이 이런 비용을 감수해 가며 2.0 부터 제네릭을 지원하는 가장 큰 이유는 제네릭 컬렉션 클래스를 지원하기 위함이다 .

Page 15: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

15 / 38

제너릭 컬렉션

• 기본적인 자료의 집합을 관리하는 컬렉션은 모든 응용 프로그램에 필수적인 자료 구조 이다 .

• C# 은 처음부터 컬렉션 클래스를 지원 하였지만 , 제네릭 이전에는 일반 클래스였으며 일반 클래스에는 문제점이 있었다 .

Page 16: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

16 / 38

일반 클래스의 제네릭

using System;using System.Collections;

class CSTest{ static void Main() { ArrayList ar = new ArrayList(10); ar.Add(1); ar.Add(2.34); ar.Add("string");

int i = (int)ar[0]; double d = (double)ar[1]; string str = (string)ar[2];

Console.WriteLine("{0}, {1}, {2}", i, d, str); }}

1. 일반 컬렉션의 요소 타입은 object 이므로 임의의 요소를 저장할 수 있다 .

2. 어떤 객체든지 컬렉션에 넣을 수 있으며 이를 막을 수 있는 문법적인 방법이 전혀 없다 .

3. 예제에서 정수 , 실수 , 문자열을 하나의 배열에 넣을 수 있다 .

빼 낼 때는 과다한 캐스팅이 발생함

Page 17: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

17 / 38

• 컬렉션에 저장된 정보를 읽을 때 object 타입으로 리턴되므로 원하는 타입으로 캐스팅해야 한다 .

• 그러기 위해서는 컬렉션에 어떤 타입의 객체가 저장되어 있는지 일일이 기억해 놓거나 아니면 실행 중에 타입을 조사해야 하는데 이 작업이 아주 번거롭다 . 부모는 자식을 가리킬 수 있기 때문에 넣을 때는 아무 것이나 넣을 수 있지만 빼낼 때는 그렇지 못한 것이다 . 위 코드에서 캐스트 연산자를 빼고 int I = ar[0]; 로 수정하면 에러가 난다 .

일반 클래스의 컬렉션 관련 문제점

Page 18: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

18 / 38

• 캐스팅을 잘못하면 위험해진다 . ar[0] 를 string 타입으로 캐스팅하여 읽으면 정수가 가리키는 번지를 읽으려고 시도할 것이므로 잘못하면 다운될 수도 있다 .

• 컴파일러가 이런 위험한 문장을 에러로 처리할 수 없는 이유는 ar[0] 에 어떤 타입의 객체가 저장될지 컴파일 중에는 알 방법이 없기 때문이다 .

• C# 은 이런 문제를 해결하기 위하여 is, as 같은 연산자를 제공하기는 하지만 이 방법은 실행 중에만 쓸 수 있어 불편할 뿐만 아니라 완전하지도 않다 .

일반 클래스의 컬렉션 관련 문제점

Page 19: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

19 / 38

• 값 타입을 컬렉션에 저장할 때는 object 타입으로 변환하는 박싱이 필요하고 꺼낼 때는 언박싱이 필요한다 . 이 처리는 컴퓨터가 자동으로 해 주지만 성능상의 불이익은 피할 수없다 .

• 정수 값 하나늘 넣어도 object 로 바꾼 후에 넣기 때문에 메모리도 많이 소모되고 속도도 느리다 .

• 이러한 문제가 발생하는 근본 원인은 컬렉션에 저장될 수 있는 타입이 너무 일반적이어서 컴파일러가 잘못된 코드를 적발해 낼 수 있는 정보가 충분하지 않기 때문이다 .

• 제너릭을 사용하면 타입 인수로 처리 대상을 지정할 수 있으므로 위의 문제를 해결할 수 있다 .

일반 클래스의 컬렉션 관련 문제점

Page 20: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

20 / 38

•닷넷는 기존의 컬렉션 클래스를 대체할 수 있는 제네릭 컬렉션을 제공한다 .

• ArrayList 의 제네릭 버전은 List<T> 이다 .

일반 클래스의 컬렉션 관련 문제점

Page 21: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

21 / 38

ArrayList의 제네릭 버전인 List<T> 예제(문자열의 컬렉션 관리 )using System;using System.Collections.Generic;

class CSTest{ static void Main() { List<string> ar = new List<string>(10); ar.Add(" 이승만 "); ar.Add("박정희 "); ar.Add("최규하 "); //ar.Add(1234); //ar.Add(5.678); foreach (string s in ar) Console.Write(s + ","); }}

1. 네임 스페이스 : System.Collections.Generic2. ar 은 List<string> 타입으로 선언되었으므로 문자열만

저장할 수 있다 .

Page 22: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

22 / 38

제네릭의 장점

• 컬렉션에 저장 가능한 타입이 선언 시점에 명시되므로 컴파일러는 어떤 타입이 안전하게 저장될 수 있는지 분명하게 알 수 있다 .

• 실행 중이 아닌 컴파일 타임에 수행할 수 있다는 점이 제네릭의 장점이다 .

• 저장되는 타입이 정해져 있으므로 꺼낼 때도 캐스팅을 할 필요가 업으며 실수를 할 잠재적인 위험도 없다 .

Page 23: 06a 장 .  컬렉션

c#

04 장 . 제어문과 예외처리

23 / 38

제네릭의 장점

• List 의 프로퍼티나 메서드는 ArrayList 와 거의 동일하다 .

• ArrayList 는 비제네릭 버전이고 List 는 이 클래스를 새로 만든 제네릭 버젼이기 때문에 인터페이스가 비슷하다

•닷넷 공식 문서에서는 가능하면 제레릭 버전을 쓸 것을 권장한다 .

• List 가 가장 자주 사용되는 범용적인 컬렉션이지만 이외에도 이중 연결 리스트를 제공하는 LinkedList 가 있고 , Queue, Stack, Dictionary 같은 제네릭 컬렉션도 제공한다 .