1 / 23

06a 장 . 컬렉션

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

hogan
Download Presentation

06a 장 . 컬렉션

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. C# 06a장. 컬렉션

  2. 컬렉션 : 변수들의 조직적인 집합 • 컬렉션 클래스 : 컬렉션 집합을 저장하고 관리하는 클래스 • 컬렉션의 종류 : 배열, 배열 리스트, 해시 테이블, 큐, 스택 • 컬렉션의 네임 스페이스 : System.Collections • 비제네릭 컬렉션 ; System.Collections • 제네릭 컬렉션 : System.Collections.Generic

  3. 제너릭 제너릭(Generic) : 타입 인수를 사용하여 일반화된 클래스나 메서드를 정의하는 기법 C# 2.0 부터 지원 C++의 템플릿과 유사

  4. 제너릭이 필요한 이유 class CSTest { static void Main() { WrapperIntgi = new WrapperInt(1234); gi.OutValue(); WrapperStringgs = new WrapperString("문자열"); gs.OutValue(); } } using System; class WrapperInt { int Value; public WrapperInt() { Value = 0; } public WrapperInt(intaValue) { Value = aValue; } public int Data { get { return Value; } set { Value = value; } } public void OutValue() { Console.WriteLine(Value); } } class WrapperString { string Value; public WrapperString() { Value = null; } public WrapperString(string aValue) { Value = aValue; } public string Data { get { return Value; } set { Value = value; } } public void OutValue() { Console.WriteLine(Value); } } 1. int형을 정의한 클래스 2 개의 클래스(WrapperInt, WrapperString)가 모두 내부 코드는 동일하다.  제네릭으로 간단히 정의 가능 2. string 형을 정의한 클래스

  5. 제너릭이 필요한 이유 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개의 클래스(Wrapper<T>)로 정의 선언문의 <T>가 타입 인수(Type Parameter)임 T는 실제 타입을 위한 자료 표시이며실제 타입은 객체를 생성할 때 지정된다. 타입 인수는 모든 곳에 사용 가능함(필드, 프로퍼티의 타입, 메서드의리턴값, 메서드의 인수 타입 등) 예제에서는 3군데에서 사용됨- Value 필드- 생성자의 인수 aValue- Data 프로퍼티의 타입

  6. T의 실제 타입 지정 하는 곳 : 객체 생성문의<>괄호 안에 지정 • 앞의 예에서 Wrapper<int>, Wrapper<string>으로 정의하였다. 이와 같이 Wrapper<double>, Wrapper<double> 같이 많은 클래스를 정의할 수 있다. • 제네릭은 클래스를 찍어내는 형틀이다. class Wrapper<int> { int Value; public Wrapper() { Value =0; } public Wrapper(string aValue) { Value = aValue; } public int Data int class Wrapper<string> { string Value; public Wrapper() { Value =null; } public Wrapper(string aValue) { Value = aValue; } public string Data class Wrapper<T> { T Value; public Wrapper() { Value = default(T); } public Wrapper(T aValue) { Value = aValue; } public T Data string double class Wrapper<double> { double Value; public Wrapper() { Value =0.0; } public Wrapper(double aValue) { Value = aValue; } public double Data

  7. 개방형 타입 : 아직 타입이 결정되지 않은 Wrapper<T> • 폐쇄형 타입 : 타입이 결정된 Wrapper<int> • 개방형 타입은 클래스를 만드는 도구일 뿐 실제 클레스는 아니므로 객체를 생성하지 못한다. • 제네릭 타입 구체화(Generic Type Instantiation): 개방형 타입의 타입 인수를 지정하여 폐쇄형 타입인 클래스를 생성하는 것 • T가 값 타입일 경우 : 컴파일러가 각 타입별로 구체화 한다. • T가 참조 타입일 경우 : 하나의 클래스만 생성되고 모든 참조 타입에 대해 생성된 클래스를 재사용한다. • default 키워드 : T의 기본값을 정의함 • 제네릭에서는T 가 어떤 타입이 될지 미리 알 수 없으므로 Value=0,Value=null식으로 상수를 대입할 수 없다. •  default(T)라는 표현식으로 T 에 따른 기본값을 표현한다.

  8. 제너릭을 이용한 2 개의 값 교환 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); } } 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); } }

  9. 제약 조건 • 제너릭 타입 인수 T는 별다른 지정이 없으면 모든 타입을 적용할 수 있다. • 제약 조건은 제네릭 선언문에 where 와 함께 지정하며 다음과 같은 종류가 있다.

  10. 값 타입만 가능한 예제(제네릭) 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줄을 실행 시켰을 때 메시지

  11. 앞의 예는 Wrapper 제네릭T는 값 타입만 가능하다. • Wrapper<int>는 가능하지만 Wrapper<string>이나 Wrapper<Human>은 사용할 수 없다. • where T:class 로 바꾸면 T는 참조 타입만 사용할 수 있고 값 타입은 사용할 수 없게 된다. • 제약 조건 중 가장 실용적인 것은 where T: base 형식이다. 이 조건은 T를 base나 base 파생 클래스로 제한한다.

  12. 제약 조건 where T:base using 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.Intro호출 // 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가 동작하자 않아 컴파일 거부가 된다.

  13. 제약 조건 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); } } 제약 조건 없이 제네릭으로 작성한 예제임 man 을 Human으로 캐스팅하여 성공하면 호출하고 그렇지 않으면 아무런 동작도 하지 않는다 그렇기 때문에 문자열 같은 잘못된 타입이 전달되어도 호출되었다가 그냥 리턴 함.  제약 조건은 컴파일할 때 타입을 체크하여 불가능한 호출을 원천적으로 차단하고 캐스팅을 쵷소화 하는 역할을 한다.

  14. 제너릭 컬렉션 • 제네릭은 원래 C# 언어의 스펙에 포함되어 있던 기능이 아니다. • 제네릭은 문법을 복잡하게 만들고 컴파일을 느리게 만드는 주범인데다 컴파일러까지 복잡해져 비용이 많이 든다. • C#이 이런 비용을 감수해 가며 2.0 부터 제네릭을 지원하는 가장 큰 이유는 제네릭 컬렉션 클래스를 지원하기 위함이다.

  15. 제너릭 컬렉션 • 기본적인 자료의 집합을 관리하는 컬렉션은 모든 응용 프로그램에 필수적인 자료 구조 이다. • C#은 처음부터 컬렉션 클래스를 지원 하였지만, 제네릭 이전에는 일반 클래스였으며 일반 클래스에는 문제점이 있었다.

  16. 일반 클래스의 제네릭 using System; using System.Collections; class CSTest { static void Main() { ArrayListar = new ArrayList(10); ar.Add(1); ar.Add(2.34); ar.Add("string"); inti = (int)ar[0]; double d = (double)ar[1]; string str = (string)ar[2]; Console.WriteLine("{0}, {1}, {2}", i, d, str); } } 일반 컬렉션의 요소 타입은 object 이므로 임의의 요소를 저장할 수 있다. 어떤 객체든지 컬렉션에 넣을 수 있으며 이를 막을 수 있는 문법적인 방법이 전혀 없다. 예제에서 정수, 실수, 문자열을 하나의 배열에 넣을 수 있다. 빼 낼 때는 과다한 캐스팅이 발생함

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

  18. 일반 클래스의 컬렉션 관련 문제점 • 캐스팅을 잘못하면 위험해진다. ar[0]를 string 타입으로 캐스팅하여 읽으면 정수가 가리키는 번지를 읽으려고 시도할 것이므로 잘못하면 다운될 수도 있다. • 컴파일러가 이런 위험한 문장을 에러로 처리할 수 없는 이유는 ar[0]에 어떤 타입의 객체가 저장될지 컴파일 중에는 알 방법이 없기 때문이다. • C#은 이런 문제를 해결하기 위하여 is, as 같은 연산자를 제공하기는 하지만 이 방법은 실행 중에만 쓸 수 있어 불편할 뿐만 아니라 완전하지도 않다.

  19. 일반 클래스의 컬렉션 관련 문제점 • 값 타입을 컬렉션에 저장할 때는 object 타입으로 변환하는 박싱이 필요하고 꺼낼 때는 언박싱이필요한다. 이 처리는 컴퓨터가 자동으로 해 주지만 성능상의 불이익은 피할 수없다. • 정수 값 하나늘 넣어도 object로 바꾼 후에 넣기 때문에 메모리도 많이 소모되고 속도도 느리다. • 이러한 문제가 발생하는 근본 원인은 컬렉션에 저장될 수 있는 타입이 너무 일반적이어서 컴파일러가 잘못된 코드를 적발해 낼 수 있는 정보가 충분하지 않기 때문이다. • 제너릭을 사용하면 타입 인수로 처리 대상을 지정할 수 있으므로 위의 문제를 해결할 수 있다.

  20. 일반 클래스의 컬렉션 관련 문제점 • 닷넷는 기존의 컬렉션 클래스를 대체할 수 있는 제네릭 컬렉션을 제공한다. • ArrayList의 제네릭 버전은 List<T> 이다.

  21. 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 + ","); } } 네임 스페이스 : System.Collections.Generic ar은 List<string>타입으로 선언되었으므로 문자열만 저장할 수 있다.

  22. 제네릭의 장점 • 컬렉션에 저장 가능한 타입이 선언 시점에 명시되므로 컴파일러는 어떤 타입이 안전하게 저장될 수 있는지 분명하게 알 수 있다. • 실행 중이 아닌 컴파일 타임에 수행할 수 있다는 점이 제네릭의 장점이다. • 저장되는 타입이 정해져 있으므로 꺼낼 때도 캐스팅을 할 필요가 업으며 실수를 할 잠재적인 위험도 없다.

  23. 제네릭의 장점 • List의 프로퍼티나 메서드는ArrayList와 거의 동일하다. • ArrayList는 비제네릭 버전이고 List는 이 클래스를 새로 만든 제네릭 버젼이기 때문에 인터페이스가 비슷하다 • 닷넷 공식 문서에서는 가능하면 제레릭 버전을 쓸 것을 권장한다. • List가 가장 자주 사용되는 범용적인 컬렉션이지만 이외에도 이중 연결 리스트를 제공하는 LinkedList가 있고, Queue, Stack, Dictionary 같은 제네릭 컬렉션도 제공한다.

More Related