일반화 프로그래밍이란


특수한 개념으로부터 공통된 개념을 찾아 묶는것을 일반화라고하는데, 일반화프로그래밍에서 일반화하는 대상은 데이터형식이다. 


void CopyArray( int[] source , int[] target)

{

for( int i = 0; i < source.Length; i++)

target[i] = source[i];

}


int형 배열을 복사하는 CopyArray메소드 에서 만약 문자열을 복사하는 메소드가 필요할때 , 일반화 프로그래밍을 모른다면


void CopyArray( string[] source , string[] target)

{

for( int i = 0; i < source.Length; i++)

target[i] = source[i];

}


이런식으로 메소드를 오버로딩해야한다. 하지만 일반화프로그래밍을 알고있다면, 이 메소드는 내부 논리는 같고 매개변수로 입력되는 배열의 형식만 다를뿐이기 때문에, 오버로딩하지않고도 모든 형식을 지원할 수 있다.


일반화 메소드의 선언


한정자 반환형식 메소드이름 <형식매개 변수> ( 매개 변수 목록)

{

//

}


ex)


void CopyArray<T> ( T[] source , T[] target)

{

for( int i =0; i < source.Length; i++)

target[i] = source [i];

}


일반화 클래스의 선언


일반화 클래스는 데이터형식을 일반화한 클래스이다. 


class 클래스이름 < 형식매개변수>

{

//

}


ex)


class Array_Int

{

private int[] array;

//

public int GetElement (int index) { return array[index]; }

}


class Array_Double

{

private double[] array

//

public double GetElement( int index) { return array[index]};

}


두 클래스는 똑같은 기능을 하는 클래스이지만, 내부적으로 사용하는 데이터의 형식이 다르기때문에, 두개의 클래스로 분리해 작성하였다.

이때에 일반화 클래스를 사용하면 간단하게 개선 가능하다.


class Array<T>

{

private T[] array;

//

public T GetElement ( int index) { return array[index]; } 

}


객체 생성시에는 이렇게 사용한다.


Array<int> array1 = new Array<int>();

Array<double> array2 = new Array<double>();


형식 매개 변수 제약시키기


형식매개변수 T는 모든 데이터형식을 대신할수 있다. 형식매개변수의 조건에 제약을 주면, 특정 조건의 데이터형식만 사용 가능하게 만들 수 있다.

헤더에 where 절을 추가하여 형식매개변수를 선언할 수 있다.


where 형식매개변수 : 제약조건


ex)


T가 Myclass에서 상속받은 형식이어야할때


class MyList<T> where T : Myclass

{

//

}


T가 값형식이어야할때


void CopyArray<T>( T[] source , T[] target ) where T : struct

{

for ( int i = 0; i < source.Length; i++)

target[i] = source[i]

}


제약조건 정리


where T : struct - T는 값 형식이어야한다.

where T : class - T는 참조 형식이어야 한다.

where T : new() - T는 반드시 매개변수가 없는 생성자가 있어야한다

where T : 기반클래스이름 - T는 명시한 기반 클래스의 파생클래스이거나 기반클래스와 같아야한다,

where T : 인터페이스 이름 - T는 명시한 인터페이스를 반드시 구현해야한다. 인터페이스 이름에는 여러개의 인터페이스를 명시할 수도 있다.

where T : U - T는 또 다른 형식 매개 변수 U 로부터 상속받은 클래스여야한다.


ex)


where T : 기반클래스이름


class Parent

{

public string  name { set; get; }

public Parent() { name = "부모클래스" ; }

}


class Children : Parent 

{

public Children() { name = " 자식클래스" ; } 

}


class List <T> where T : Parent

{

public T[] array;

public List() { array = new T[2]; }

}


class program

static void Main( string[] args)

{

List<Parent> list = new List<Parent>();


list.array[0] = new Parent();

list.array[1] = new Children();


Console.WriteLine(list.array[0].name);

Console.WriteLine(list.array[1].name);

}

}


where T : new()


public static T CreateInstance<T>() where T : new()

{

return new T();

// CreateInstance<T>() 메소드는 기본 생성자를 가진 어떤 클래스의 객체라도 생성가능. 기본생성자가 없다면 컴파일에러


where T : U


class BaseArray<U> where U : Base

{

public U[] Array { get; set;}

public BaseArray( int size)

{

Array = new U[size];

}

public void CopyArray<T>(T[] Source) where T : U

{

Source.CopyTo(Array, 0);

}

}


일반화 컬렉션


ArrayList , Queue , Stack , Hashtable 이 컬렉션들은 모두 object 형식을 기반으로 했다. 따라서 모두 object 형식으로 형식변환이 가능하다. object형식을 기반으로 하기때문에 태생적으로 성능문제를 안고있는데, 일반화 컬렉션은 이 object 형식의 컬렉션이 갖고있던 문제들을 말끔히 해결한다. 일반화 컬렉션은  일반화에 기반해서 만들어져있기때문에 컴파일시 컬렉션에서 사용할 형식이 결정되고, 쓸데없는 형식변환을 일으키지 않는다.// 성능저하가 해결된다

대표적인 일반화컬렉션은 List<T> , Queue<T> , Stack<T> , Dictionary<TKey,TValue> 이 있다.


List<T>


List<T>는 ArrayList와 같은 기능을 하고, 사용법 역시 동일하지만, List<T>는 아무 형식의 객체를 마구 집어 넣을 수 있었던 ArrayList와 달리 형식매개변수로 입력한 형식 외에는 입력을 허용하지 않는다.


ex)


List<int> list = new List<int>();

List.Add(0) = 1; 

List.Add(1) = 2;

List.Add(2) = 3;

List.RemoveAt(1); // 1번에있는 데이터를 지움

List.Insert(1,2); // 1번에 int형식의 2 데이터를 넣음


Queue<T>


Queue<T> 는 Queue와 같은 기능을 하고, 사용법이 동일하다. 형식매개변수를 요구한다는 점만 다르다.


ex)


Queue<int> queue = new Queue<int>();


queue.Enqueue(1);

queue.Enqueue(2);

queue.Enqueue(3);


int a = queue.Dequeue(); // a에는 1 저장

int b = queue.Dequeue(); // b에는 2 저장


Stack<T>


Stack<T> 는 Stack와 같은 기능을 하고, 사용법이 동일하다. 형식매개변수를 요구한다는 점만 다르다.


ex)


Stack<int> stack = new Stack<int>();


stack.Push(1);

stack.Push(2);

stack.Push(3);


int a = stack.Pop(); // a에는 3 저장

int b = stack.Pop(); // b에는 2 저장


Dictionary<TKey,TValue>


Dictionary<TKey,TValue> 는 Hashtable의 일반화 버전이다. 


ex)


Dictionary<string, string> dic = new Dictionary<string,string>();


dic["하나"] = "one";

dic["둘"] = "two";

dic["셋"] = "three";


Console.WriteLine(dic["하나"]); // one 출력


foreach를 사용할 수 있는 일반화 클래스


foreach가 사용가능한 객체를 만들기위해서는 클래스가 IEnumerable 인터페이스와 IEnumerator 인터페이스를 상속하고, 이들의 메소드와 프로퍼티를 구현해야하는데 일반화 클래스에서 이 인터페이스를 구현하게되면 기껏 일반화 해서 얻은 성능향상을 저해하게된다. 하지만 IEnumerable<T>인터페이스와 IEnumerator 인터페이스를 상속하게되면, 성능저하없이 foreach 가 사용 가능한 일반화 클래스를 만들 수 있다.


IEnumerator <T>의 메소드 및 프로퍼티


IEnumerator GetEnumerator() IEnumerator 형식의 객체를 반환

IEnumerator<T> GetEnumerator() IEnumerator<T> 형식의 객체를 반환 // 두개 모두 구현해야한다


IEnumerable <T>의 메소드 및 프로퍼티


boolean MoveNext() 다음 요소로 이동. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true를 반환한다

void Reset() 컬렉션의 첫번째 위치 "앞"으로 이동한다. 

Object Current{ get;} 컬렉션의 현재 요소를 반환한다

T Current{ get; } 컬렉션의 현재 요소를 반환한다




배열의 선언


데이터형식[] 배열이름 = new 데이터형식 [ 용량 ];

ex) int[] arr = new int[3];


배열을 초기화하는 세가지 방법


string[] array1 = new string[2] {"안녕", "hello", "halo"};


string[] array2 = new string[]  {"안녕", "hello", "halo"}; // 배열을 선언과 동시에 초기화할때는 [] 안에 용량을 명시하지않아도됨


string[] array3 =  {"안녕", "hello", "halo"};


System.Array


C#에서는 모든 것이 객체이며 , 기반이되는 형식이 있다. 배열또한 객체이며 , .NET 프레임워크의 CTS(Common Type System}에서 배열은 System.Array 클래스에 대응된다.

// GetType() 메소드를 사용하면 객체의 클래스 타입을 알 수 있고, BaseType() 메소드를 사용하면 객체의 기반클래스의 타입을 알 수 있다.

ex ) int[] array = new int[2]; 에서 array.GetType() 의값은 System.Int32[] 이며 array.BaseType(); 의 값은 System.Array 이다.

* 객체의 클래스 타입을 알게 되면 , 그 클래스에 포함되어있는 메소드와 프로퍼티를 모두 이용할 수 있게되므로, 유용하게 쓰일 수 있다.


2차원 배열의 선언


데이터형식[ , ] 배열이름 = new 데이터형식 [ 2차원길이 , 1차원길이 ];


ex ) int [ , ] array = new int [2,3]; 은 2X3 배열을 만든다. (행이 2 , 열이 3)


2차원배열의 초기화 또한 1차원배열초기화 방법처럼 3개가있고 , 동일하게 사용하면된다.


ex) int[,] array = new int[2,3] = { {1,2,3} , {4,5,6} }; 


가변배열 


가변배열은 영어로 jagged array 인데 , 이는 들쭉날쭉한 배열이라는 뜻이다. 배열의 길이를 늘였다 줄였다 할 수 있는 배열이라는 뜻이 아니다.

가변배열의 요소는 배열이다 . 배열은 객체이므로 하나하나의 배열 객체를 담고있는것이된다.


데이터형식[][] 배열이름 = new 데이터형식[가변배열의 용량(행의길이)] [ ];


ex)


int[][] jagged = new int[3][];

jagged[0] = new int[5] { 1,2,3,4,5 }; // 배열은 객체이다

jagged[1] = new int[] {10,20,30};

jagged[2] = new int[] {10,20};


가변배열또한 선언과 동시에 초기화 할 수 있다. 


int[][] jagged = new int[2][]

{

new int[] { 1000 , 2000 , 3000 }, // ; 가아닌 , 를 사용한다.

new int[2] { 1,3 }; // 마지막에는 ; 사용

};


컬렉션


컬렉션은 같은 성격을 띄는 데이터의 모음을 담는 자료구조이다. 배열또한 컬렉션 자료구조의 일부분이다.  컬렉션에 속한 ArrayList , Queue , Stack , Hashtable  클래스들에 대해 알아보자.



ArrayList


ArrayList 컬렉션은 컬렉션요소에 접근할때는 [] 연산자를 이용하고, 특정 위치에 있는 요소를 임의로 할당할 수있다. 또한 컬렉션을 생성할 때 , 용량을 따로 지정할 필요 없이 자동으로 용량이 늘었다가 줄었다가 한다.

ArrayList의 가장 중요한 메소드는 Add() , RemoveAt() , Insert() 이렇게 세가지인데, Add() 메소드는 컬렉션의 가장 마지막에있는 요소 뒤에 새 요소를 추가하는 메소드이고, RemoveAt()메소드는 특정 인덱스에 있는 요소를 제거하고, Insert() 메소드는 원하는 위치에 새 요소를 삽입한다.


ex)


ArrayList list = new ArrayList();

list.Add(10);

list.Add(20);

lsit.Add(30);

list.RemoveAt(1); // 20을 삭제

list.Insert(1,25); // 25를 1번 인덱스에 삽입


Queue 


Queue 컬렉션은 데이터나 작업을 차례대로 입력해뒀다가 들어온 서대로 하나씩 꺼내 처리하기 위해 사용한다. 프린터가 여러 문서를 출력할때 , 인터넷 동영상 스트리밍 서비스에서 컨텐츠를 버퍼링할 때 등에 사용된다. Queue에서 데이터를 입력하는것은 오로지 맨뒤에서만 이루어지며, 이는 Enqueue() 메소드를 이용해서 이루어진다.


ex)


Queue que = new Queue();

que.Enqueue(1);

que.Enqueue(2):

que.Enqueue(3);


데이터를 꺼낼때는 Dequeue() 메소드를 통해 이루어진다. Dequeue()메소드로 데이터를 꺼내면 , 실제로 데이터를 컬렉션에서 꺼내게 된다( 컬렉션에서는 꺼낸 데이터가 사라진다). 맨앞에서부터 꺼내진다.


ex)


int a = que.Dequeue(); // a에 1 값 저장.

int b = que.Dequeue(); // b에 2 값 저장.


Stack


Stack은 먼저 들어온 데이터가 나중에 나가고 , 나중에 들어온 데이터는 먼저 나가는 구조의 컬렉션이다.

Stack에 데이터를 넣을 때는 Push() 메소드를 이용하고. 데이터를 꺼낼때는 Pop() 메소드를 이용한다. 


ex)


Stack stack = new Stack();

stack.Push(1); // 가장 나중에나올 데이터

stack.Push(2);

stack.Push(3); // 가장 나중에 나올 데이터


int a = (int)stack.Pop(); // a에 3 저장


Hashtable


Hashtable 컬렉션은 Key와 Value 의 쌍으로 이루어진 데이터를 다룰때 사용한다. 예를들어 "book"을 Key로 , "책"을 Value로 입력하는 식이다. 

Hashtable은 탐색속도가 빠르고, 사용하기도 편하다.


ex)


Hashtable ht = new Hashtable();

ht["book"] = "책";

ht["cook"] = "요리사";

ht["tweet"] = "지저귀다";

Console.WriteLine(ht["book"]); // 책 출력


Hashtable 형식은 문자열뿐만아니라 int 형식 , double 형식 등등 어떤형식이든 Key로 활용할 수 있다. 


인덱서


인덱서는 index 를 이용해서 객체 내의 데이터에 접근할 수 있게 해주는 프로퍼티이다. 객체를 마치 배열처럼 사용할 수 있게 해준다.


인덱서의 선언


class 클래스이름

{

한정자 인덱서형식 this [형식 index] // 인덱스의 식별자가 꼭 index일 필요없으므로 적당한 이름을 사용하면 된다.

{

get

{

// index를 이용하여 내부 데이터 반환

}

set

{

// index를 이용하여 내부 데이터 저장

}

}

}


ex)


class MyList

{

private int[] array;


public MyList()

{

array = new int[3];

}


public int this [int index]

{

get

{

return array[index];

}


set

{

if (index >= array.Length)

{

Array.Resize<int>(ref array, index+1);

Console.WriteLine("Array Resized : {0}" , array.Length);

}


array[index] = value;

}

}

}


foreach가 가능한 객체 


foreach문은 IEnumerable 과 IEnumerator인터페이스를 상속하는 클래스만 지원한다. 배열과 컬렉션은 이 두개의 인터페이스를 상속하는 클래스이기 때문에 사용 가능하다. 


IEnumerable 인터페이스가 가지고 있는 메소드 


IEnumerator GetEnumerator() - IEnumerator 형식의 객체를 반환한다. (컬렉션을 반복하는 열거자를 반환한다)


IEnumerator 인터페이스가 가지고 있는 메소드


boolean MoveNext() - 다음 요소로 이동한다. 컬렉션의 끝을 지난 경우에는 false, 이동이 성공한 경우에는 true 를 반환한다.


void Reset() - 컬렉션의 첫번째 위치의 "앞"으로 이동한다. ex) 첫번째 위치가 0이라면 -1 로 이동, 첫번째 위치로의 이동은 MoveNext()를 호출한 다음에 이루어진다.


Object Current { get; } - 컬렉션의 현재 요소를 반환한다.


ex)


//IEnumerable 멤버


public IEnumerator GetEnumerator()

{

for(int i = 0; i < array.Length; i++)

{

yield return (array[i]);

}

}


//IEnumerator멤버


int position = -1;


public bool MoveNext()

{

if(positoin == array.Length-1)

{

reset();

return false;

}


position ++;

return (position< array.Length);

}


public object Current()

{

get

{

return array[position];

}

}


public void Reset()

{

position = -1;

}



'C#언어 > 뇌자극C#5.0' 카테고리의 다른 글

C# : Chapter - 11 예외 처리하기  (0) 2018.01.26
C# : Chapter -10 일반화 프로그래밍  (0) 2018.01.14
C# : Chapter -8 프로퍼티  (0) 2018.01.08
C# : Chapter -7 인터페이스  (0) 2017.12.16
C# : Chapter -6 클래스  (0) 2017.12.08

프로퍼티의 선언


프로퍼티를 사용하면 get , set 메소드를 생성하지 않아도 숨겨진 필드에 값을 할당하거나 불러올수있다.

-데이터의 오염에대해선 메소드만큼 안전하고, 데이터를 다룰때에는 필드처럼 간결하다.


class 클래스이름

{

데이터형식 필드이름;

접근한정자 데이터형식 프로퍼티 이름

{

get

{

return 필드이름;

}

set

{

필드이름 = value;

}

}

}


get 접근자는 필드로부터 값을 읽어오고, set 접근자는 필드에 값을 할당한다. value는 선언하지않아도 C#컴파일러에서 매개변수로 간주한다.


class Myclass

{

private int myField;

public int MyFeild

{

get

{

return myField;

}

set

{

myField = value;

}

}

}


MyClass obj = new MyClass;

obj.MyField = 3;

Console.WriteLine( obj.MyField );


set 접근자를 구현하지 않으면, 읽기전용 프로퍼티 생성이 가능하다.

get 접근자를 구현하지 않으면 쓰기전용 프로퍼티 생성이 가능하지만, 클래스를 사용할 프로그래머들에게 쓰기전용 프로퍼티의 용도와 동작결과를 확인할 수 있는 방법을 알려줘야한다.


자동 구현 프로퍼티


아무 논리가 섞여있지않고, 단지 읽고쓰기는 프로퍼티를 생성할때는 자동 구현 프로퍼티를 사용하면 된다.


public class NameCard

{

public string Name

{

get; set;

}

public string PhoneNumber

{

get; set;

}

}


프로퍼티와 생성자


객체를 초기화할 때 , 매개변수를 입력해서 초기화할 수도 있지만, 프로퍼티를 사용해서 초기화할 수도 있다.


클래스이름 인스턴스 = new 클래스이름()

{

프로퍼티1 = 값, // 세미콜론 ; 이 아니고 콤마 , 를 사용한다.

프로퍼티2 = 값,

프로퍼티3 = 값

};


객체를 생성할 떄 , 프로퍼티 = 값 목록에 객체의 모든 프로퍼티가 올 필요는 없다. 초기화하고 싶은 프로퍼티만 넣어서 초기화하면 된다.


BirthdayInfo birth = new BirthdayInfo() // 메인클래스에서 객체 생성

{

Name = 서현,

Birthday = new DateTime(1991, 6 , 28)

};


무명 형식의 선언


이름이 없는 형식으로 선언한다. 이렇게 생성된 객체는 수정이 불가하다.


var myInstance = new { Name = "박상현" , Age = "17" };


Console.WriteLine( myInstance.Name , myInstance.Age );


인터페이스의 프로퍼티


인터페이스는 메소드뿐만아니라 프로퍼티와 인덱서도 가질 수 있다.인터페이스의 프로퍼티 선언은 당연히 구현을 갖지 않는다.

인터페이스의 프로퍼티 선언은 클래스의 자동구현 프로퍼티 선언과 그 모습이 동일하다.


인터페이스에서의 프로퍼티 선언


interface 인터페이스 이름

{

public 형식 프로퍼티이름1

{

get; set;

}


public 형식 프로퍼티이름2

{

get; set;

}


//

}


EX)


interface Iproduct

{

string ProductName

{

get;

set;

}

}


class Product : Iproduct

{

private string productName;


public string ProductName // 프로퍼티의 구현

{

get { return productName; }

set { productName = value; }

}

}


추상클래스와 프로퍼티


추상클래스는 클래스처럼 구현된 프로퍼티를 가질수도 , 인터페이스처럼 구현되지 않은 프로퍼티도 가질 수 있다. 이것을 추상 프로퍼티라고한다.

이는 추상메소드와 같이 단지 파생클래스가 해당 프로퍼티를 구현하도록 강제한다.


추상클래스에서의 프로퍼티선언


abstract class 추상클래스 이름

{

abstract 데이터형식 프로퍼티이름

{

get;

set;

}

}


EX)


abstract class Product

{

private static int serial = 0;

public string SerialID // 구현을 가진 프로퍼티

{

get { return String.Format(" {0:d5}" , serial++); }

}


abstract public DateTime ProductDate // 구현을 가지지않은 프로퍼티

{

get;

set;

}

}


class MyProduct : Product

{

public override DateTime ProductDate

{

get;

set;

}

}



'C#언어 > 뇌자극C#5.0' 카테고리의 다른 글

C# : Chapter -10 일반화 프로그래밍  (0) 2018.01.14
C# : Chapter -9 배열 , 컬렉션 , 인덱서  (0) 2018.01.12
C# : Chapter -7 인터페이스  (0) 2017.12.16
C# : Chapter -6 클래스  (0) 2017.12.08
C# : Chapter -5 메소드  (0) 2017.12.08

+ Recent posts