외로운 Nova의 작업실

c# 언어 공부 - 7(일반화 프로그래밍) 본문

Programming/C#

c# 언어 공부 - 7(일반화 프로그래밍)

Nova_ 2022. 5. 6. 01:13

안녕하세요. 오늘은 저번시간에이어 c#의 일반화 프로그래밍과 예외처리를 배워보도록 하겠습니다.

 

<일반화 프로그래밍>

일반화 프로그래밍은 객체들의 공통점을 찾아서 일반화 하는 프로그래밍을 말합니다.

예를들어 int[], string[], double[] 객체들의 공통점은 배열이라는 것입니다.

일반화 프로그래밍은 다음과 같은 상황일때 유용합니다.

1.배열을 copy하는 copyarray 객체를 만들었습니다.

class CopyArray
{

}

2. int[]배열을 copy하는 메소드를 만들었습니다.

class CopyArray
        {
            public void Copyint(int[] a, int[] b)
            {
                for(int i = 0; i < a.Length; i++)
                {
                    a[i] = b[i];
                }
            }
        }

3.쓰다보니 string[]배열을 copy하는 메소드가 필요합니다.

4.string[]배열을 copy하는 메소드를 만들었습니다.

public void Copyint(int[] a, int[] b)
            {
                for(int i = 0; i < a.Length; i++)
                {
                    a[i] = b[i];
                }
            }

            public void Copysting(string[] a, string[] b)
            {
                for (int i = 0; i < a.Length; i++)
                {
                    a[i] = b[i];
                }
            }

5.쓰다보니 double[] 배열을 copy하는 메소드가 필요합니다.

.

.

.

이런식으로 31가지의 copy하는 메소드를 만들게되었습니다.

 

위와같은 방법으로 메소드를 다 구현할 수 있지만.

일반화 프로그래밍을 사용하면 1가지 메소드만 구현하면 됩니다.

위 31개의 메소드들을 1개의 일반화 메소드로 바꿔보겠습니다.

class CopyArray
        {
            public void Copy<T>( T[] a, T[] b) //T는 형식 매개변수
            {
                for(int i = 0; i < a.Length; i++)
                {
                    a[i] = b[i];
                }
            }
        }

T는 형식매개변수로 int, string, double 등이 들어갈 수 있습니다.

그럼 실제로 사용도 해보겠습니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            int[] a = { 1, 2, 3 }; 
            int[] b = new int[3]; 
            
            double[] c = {1.2, 1.3, 1.4};
            double[] d = new double[3];
            
            CopyArray copy = new CopyArray();

            copy.Copy(a, b); //int형 메소드
            Console.WriteLine($"{b[0]}, {b[1]}, {b[2]}");
            
            copy.Copy(c, d); //double향 메소드
            Console.WriteLine($"{d[0]}, {d[1]}, {d[2]}");

        }
        class CopyArray
        {
            public void Copy<T>( T[] a, T[] b) //T는 형식 매개변수
            {
                for(int i = 0; i < a.Length; i++)
                {
                    b[i] = a[i];
                }
            }
        }

    }

    
   
}

 

<일반화 클래스>

클래스를 만들때도 int에 관련된 클래스를 만들었더니 string 클래스가 필요해서 string 클래스를 만들고 또 이렇게하다보니 31가지 클래스를 만드는 경우에도 일반화 할 수 있습니다.

먼저 일반화 클래스가 아닌 경우의 코드입니다.

class IntValue
{
	public int value; 
}

class StringValue
{
	public string value;
}
.
.
.
.

위를 일반화 클래스로 변경해보겠습니다.

class anyValue<T>
{
	public T value; 
}

이런식으로 또 T 형식 매개변수를 이용하여 사용할 수 있습니다.

T에는 int, stirng, double등이 들어가면 int가 들어갈경우 int형 변수 value가 만들어집니다.

그럼 일반화 클래스를 한번 사용해보겠습니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            anyValue<int> val = new anyValue<int>(); //int형 일반화 클래스 선언

            val.value = 3;

            Console.WriteLine(val.value);
            
            anyValue<stirng> val2 = new anyValue<string>(); //string형 일반화 클래스 선언

            val.value = "hello";

            Console.WriteLine(val2.value);

        }
        class anyValue<T>
        {
            public T value; 
        }
    }
}

 

<형식 매개변수 제약시키기>

일반적으로 형식 매개변수에는 모든 형식이 들어갈 수 있습니다.

하지만 종종 특정 조건을 갖춘 형식에만 대응하는 형식 매개변수가 필요할 때도 있습니다.

예를 들어, T 형식매개 변수에는 정수형만 들어가게하는 것처럼 말입니다.

그러면 string은 못들어가겠죠 정수형이 아니니까요.

이처럼 형식 매개변수를 제약할때의 방법을 알아보도록 하겠습니다.

형식 매개변수를 제약할때는 where 절을 쓰면됩니다.

예를 들어 매개변수를 값 형식 매개변수만 가능하게 하려면 아래와 같은 코드로 제약 할 수 있습니다.

class anyValue<T> where T : struct //where절로 형식 매개변수 T에 값 데이터형식만 가능하게함
        {
            public T value; 
        }

그럼 where 절에 쓸수 있는 제약조건을 정리해보도록 하겠습니다.

where T : struct T는 값 형식만 가능합니다.
where T : class T는 참조 형식만 가능합니다.
where T : new() T는 반드시 매개변수가 없는 생성자가 있어야합니다.
where T : 기반 클래스 이름 T는 명시한 기반 클래스의 파생 클래스여야합니다.
where T : 인터페이스 이름 T는 명시한 인터페이스를 구현해야합니다.
where T : U T는 또 다른 형식 매개변수 U로부터 상속받은 클래스여야합니다.

new() 까지 한번 예제를 써보도록 하겠습니다.

먼저 struct는 위에 있으니 넘어가도록 하겠습니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            anyValue<int[]> val = new anyValue<int[]>(); //배열은 참조형식으로 가능합니다.

            val.value[0] = 1;

            Console.WriteLine(val.value);

        }
        class anyValue<T> where T : class //참조형식 데이터만 가능합니다.
        {
            public T value; 
        }
    }
}
using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            anyValue<int> val = new anyValue<int>(); //int형식은 생성자가 있습니다.

            val.value = 1;

            Console.WriteLine(val.value);

        }
        class anyValue<T> where T : new() //생성자가 있는 것만 가능합니다
        {
            public T value = new T(); //생성자가 있기에 이렇게 쓸 수 있습니다.
        }
    }
}

 

<일반화컬렉션>

저번시간에 배웠던 컬렉션에도 일반화 프로그래밍을 적용할 수 있습니다.

ArrayList는 List<T>로 사용할 수 있습니다.

Queue는 Queue<T>로 사용할 수 있습니다.

Stack은 Stack<T>로 사용할 수 있습니다.

HashTable은 Dictionary<TKey, TValue>로 사용할 수 있습니다.

각 자료형에는 T에서 한정하는 객체만 넣을 수 있습니다.

아래는 선언 예제입니다.

using System;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {
            List<int> list = new List<int>(); //int형만 가능한 리스트 선언
            Queue<int> queue = new Queue<int>(); //int형만 가능한 queue 선언
            Stack<int> stack = new Stack<int>(); //int형만 가능한 stack선언 
            Dictionary<int,int> dic = new Dictionary<int,int>();//int형만 가능한 HashTable선언


        }
       
        
    }
}

 

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

저번에 인덱서를 사용하면 클래스를 배열처럼 사용 할 수 있다고 공부했습니다.

그때 foreach를 사용하려면 IEnumberable인터페이스와 IEnumerator인터페이스를 상속해야한다고 배웠죠.

일반화 클래스도 그 둘을 상속하게되면 foreach문이 가능하지만, 형식변환이 계속 이뤄지면서 오버로드가 일어납니다.

이를 위해 c#개발자들은 일반화 클래스는 IEnumerable<T>와 IEnumerator<T>를 상속받는다면 foreach문을 사용가능하게 만들었습니다.

그럼 IEnumerable와 IEnumerator 인터페이스의 만들어야할 메소드와 프로퍼티를 알아보겠습니다.

IEnumerator IEnumerable.GetEnumerator() IEnumerator 형식의 객체를 반환
IEnumerator<T> GetEnumertor() IEnumerator<T> 형식의 객체를 반환
boolean MoveNext() 다음요소로 이동합니다. 이동이성공한경우엔 true 아닌경우엔 false를 반환합니다.
void Reset() 컬렉션 첫번쨰 위치의 앞으로 이동합니다.
Objcet IEnumerator.Current{get:} 컬렉션의 현재 요소를 반환합니다. 
T Current{get:} 컬렉션의 현재요소를 반환합니다.

위와 인덱서를 활용해서 foreach문을 사용할 수 있는 배열 일반화 클래스를 만들어보겠습니다.

using System.Collections;
using System.Collections.Generic;

위 두개를 꼭 써줘야합니다.

using System;
using System.Collections;
using System.Collections.Generic;

namespace StudyCSharp
{
    class MainApp
    {

        static void Main(string[] args)
        {


        }
       
        class MyList<T> : IEnumerable<T>, IEnumerator<T>
        {
            private T[] array;
            int position = -1; //첫번째 배열 0의 앞 -1을 할당

            public MyList() //생성자메소드
            {
                array = new T[3];
            }

            public T this[int index] //인덱서 메소드
            {
                get
                {
                    return array[index];
                }
                set
                {
                    array[index] = value;
                }
            }

            public IEnumerator<T> GetEnumerator()
            {
                return this;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this;
            }

            public T Current
            {
                get
                {
                    return array[position];
                }
            }

            object IEnumerator.Current
            {
                get
                {
                    return array[position];
                }
            }
            public bool MoveNext()
            {
                if(position == array.Length-1)
                {
                    Reset();
                    return false;
                }

                position++;
                return (true);
            }

            public void Reset()
            {
                position = -1;
            }

            public void Dispose()
            {

            }


        }
    }
}

위와같이 코드를 짜면 MyList는 foreach문을 사용할 수 있는 일반화 배열 클래스가 됩니다.

 

다음시간에는 예외처리에 대해서 배워보도록 하겠습니다.

Comments