제네릭(Generic)
타입 인수를 사용하여 일반화된 클래스나 메서드를 정의하는 기법이다. 취급하는 타입만 다른 클래스나 메서드가 여러 개 필요할 때 제네릭으로 한번만 정의해 두면 비슷한 클래스나 메서드를 얼마든지 생성할 수 있다.
제네릭의 장단점
코드의 재사용을 높이고 타입 안전성을 극대화하는 장점이 있는 반면 코드의 크기가 커지고 가독성이 떨어지는 불리함이 있다.
예) 아래 예는 두 개의 클래스가 몇 부분만 다르고 내부 코드는 모두 동일하다. 취급하는 타입만 다를 뿐인데 둘을 각각의 클래스로 정의하는 것은 굉장히 비효율적이다.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { static void Main() { WrapperInt gi = new WrapperInt(1234); gi.OutValue(); WrapperString gs = new WrapperString("문자열"); gs.OutValue(); } } class WrapperInt { int Value; public WrapperInt() { Value = 0; } public WrapperInt(int aValue) { Value = aValue; } //Data 프로퍼티 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; } // Data 프로퍼티 public string Data { get { return Value; } set { Value = value; } } public void OutValue() { Console.WriteLine(Value); } } }예) 제네릭을 사용하면 위의 예를 아래와 같이 바꿀 수 있다.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { static void Main() { Wrappergi = new Wrapper (1234); gi.Outvalue(); Wrapper gs = new Wrapper ("문자열"); gs.Outvalue(); } } class Wrapper { T Value; // default 키워드는 인수가 참조형식인지 값형식인지에 따라 // 반환하는 형태(null 또는 0)을 결정해 준다. 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); } } }
제약 조건
제네릭의 타입 인수 T는 별다른 지정이 없으면 임의의 모든 타입을 적용할 수 있다. 이렇게 되면 T는 일단 object이므로 object의 메서드만 사용할 수 있는 제약이 생기는 셈이다. 타입 인수에 약간의 제약 조건을 지정하면 가능한 T의 종류가 제한되며 따라서 제한된 범위에서 메서드를 호출할 수 있는 효과가 있다.
제약 조건 |
설명 |
where T: struct | 형식 인수가 값 형식이어야 합니다. Nullable 를 제외한 임의의 값 형식을 지정할 수 있습니다. 자세한 내용은 Nullable 형식 사용(C# 프로그래밍 가이드)를 참조하십시오. |
where T : class | 형식 인수가 참조 형식이어야 합니다. 이는 모든 클래스, 인터페이스, 대리자 또는 배열 형식에도 적용됩니다. |
where T : new() | 형식 인수가 매개 변수 없는 공용 생성자여야 합니다. 다른 제약 조건과 함께 사용하는 경우 new() 제약 조건은 마지막에 지정해야 합니다. |
where T : <기본 클래스 이름> | 형식 인수가 지정된 기본 클래스이거나 지정된 기본 클래스에서 파생되어야 합니다. |
where T : <인터페이스 이름> | 형식 인수가 지정된 인터페이스이거나 지정된 인터페이스를 구현해야 합니다. 여러 인터페이스 제약 조건을 지정할 수 있습니다. 제한하는 인터페이스는 제네릭이 될 수도 있습니다. |
where T : U | T에 대해 지정한 형식 인수가 U에 대해 지정한 인수이거나 이 인수에서 파생되어야 합니다. |
예) T가 Human의 후손이라는 제약 조건이 있고 따라서 man은 Intro 메서드를 가진다는 것을 알 수 있다.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using System.Text.RegularExpressions; namespace ConsoleApplication1 { class Program { // 제약조건을 사용할 경우 public static void OutValue위의 예에서 제약 조건 where T : Human을 빼고 컴파일해보면 Outvalue 메서드 자체가 에러처리된다. 왜냐하면 제약 조건이 없으면 T는 object의 후손인 임의의 타입을 모두 받을 수 있는데 모든 타입에 Intro라는 메서드가 정의되어 있지 않기 때문이다. 제약 조건 없이 이 메서드를 제네릭으로 작성하려면 아래와 같이 해야 한다.(T man) where T : Human { man.Intro(); } static void Main() { Human A = new Human(); Student B = new Student(); String C = "나 문자열"; OutValue(A); OutValue(B); //OutValue(C); } } class Human { public virtual void Intro() { Console.WriteLine("나 사람"); } } class Student : Human { public override void Intro() { Console.WriteLine("나 학생"); } } }
public static void OutValue(T man) { Human t = man as Human; if(t != null) { t.Intro(); } }
'Development > C#' 카테고리의 다른 글
LINQ Sample (0) | 2013.01.31 |
---|---|
using 과 Dispose (0) | 2013.01.04 |
IPAddress 가져오기 (0) | 2012.12.21 |
List 매서드 재정의 (0) | 2012.12.13 |
최상위 컨트롤 (0) | 2012.12.13 |