[C#] 제네릭 ( Generics )

|

제네릭이란 .NET 2.0에서 새롭게 추가된 개념입니다.

제네릭을 통해서 형식 매개변수(type parameter)라는 개념이 도입되었습니다.  형식 매개변수를 사용해서 클래스나 매서드를 사용하면 그것이 인스턴스화 될때까지 형식지정을 연기할 수 있습니다.


즉, 동일한 기능을 수행하지만 입력받는 타입만 다를 경우 모두 다른 클래스나 메서드를 생성해야 하지만 

제네릭을 이용하면 하나만 선언해주고 클라이언트 코드에서 어떤 타입으로 사용할 것인지를 결정해주면 됩니다. 

또한 이에 따른 Boxing작업이 일어나지 않고 처리가 됩니다. C++의 템플릿 기능과 비슷하다고 보면됩니다.


간단한 예를 들어 보겠습니다.

아래의 예제는 사용자 정의 스택을 List로 구현한 것입니다.


1. 제네릭 클래스 선언

 public class HongsStack<T>
{
    private List<T> data;

    public HongsStack()
    {
        data = new List<T>();
    }

    //데이터 넣기
    public bool Push(T _value)
    {
        try
        {
            //쓰레드 안전
            lock(data)
            {
                this.data.Add(_value);
                return true;
            }
        }
        catch (Exception)
        {

            return false;
        }
    }

    //데이터 출력
    public T Pop()
    {
        try
        {
            lock (data)
            {
                if (this.data.Count.Equals(0))
                {
                    return default(T);
                }

                T returnData = this.data[this.data.Count - 1];
                this.data.RemoveAt(this.data.Count - 1);
                return returnData;
            }
        }
        catch (Exception)
        {
            
            throw;
        }
    }

    //스택 카운트 확인
    public int GetStackCount()
    {
        try
        {
            lock(data)
            {
                return this.data.Count;
            }
        }
        catch (Exception)
        {
            throw;
        }
    }
    
}
 

우선 클래스이름을 HongsStack<T> 로 선언해서 제네릭을 사용한다고 선언해줍니다.

클래스 내에서 입력받은 타입으로 변환되어야 할 모든 타입을 T로 선언해줍니다. 이렇게 해주면 클라이언트 코드에서

인스턴스화 해주면, 그때 클래스의 T 가 해당 입력받은 타입으로 모두 변환된다고 보시면 됩니다.



 2.제네릭 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
HongsStack<int> intStack = new HongsStack<int>();
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
Console.WriteLine(intStack.Pop().ToString());
Console.WriteLine(intStack.Pop().ToString());
intStack.Push(4);
Console.WriteLine(intStack.GetStackCount().ToString());
 
 
HongsStack<string> stringStatck = new HongsStack<string>();
stringStatck.Push("홍진현");
stringStatck.Push("NET");
stringStatck.Push("C#");
Console.WriteLine(stringStatck.Pop());
Console.WriteLine(stringStatck.Pop());
stringStatck.Push("ForMVP");
Console.WriteLine(stringStatck.GetStackCount().ToString());
 
//결과
//3
//2
//2
//C#
//NET
//2
cs

사용은 HongsStack<int> 나 HongsStack<string> 으로 해서 선언을 해주면 됩니다.

이처럼 동일한 기능을 수행하지만 타입이 다를경우에는 제네릭을 이용하면 유연한 클래스를 만들수 있습니다.




And

[C#] 해시테이블 vs 딕셔너리 ( hashtable vs. dictionary )

|

C#에서는 KEY 와 VALUE를 사용해서 자료를 저장하는 타입이 2가지가 있습니다.

해시테이블과 딕셔너리인데 사용법은 거의 동일하지만 내부적으로 처리하는 기술이 다릅니다.

이 두가지 타입의 기본적인 사용법과 장단점에 대해서 알아보겠습니다.


1.해시테이블 ( Hashtable)


//생성
Hashtable hashtable = new Hashtable();

//자료 추가 ( 박싱이 일어남)
hashtable.Add("Data1", new HongsClass() { Name = "홍진현1", intCount = 1 });
hashtable.Add("Data2", new HongsClass() { Name = "홍진현2", intCount = 2 });

//자료 검색
if (hashtable.ContainsKey("Data1").Equals(true))
{
    HongsClass temp = hashtable["Data1"] as HongsClass; (언박싱 처리)
    Console.WriteLine(temp.Name);
}

//Loop 전체 순회출력
foreach (string NowKey in hashtable.Keys)
{
    HongsClass temp = hashtable[NowKey] as HongsClass;
    Console.WriteLine(temp.Name);
        
}

//결과  OUTPUT
//홍진현1
//홍진현1
//홍진현2


[해시테이블의 특징]


1.Non-Generic

2.Key 와 Value 모두 Object를 입력받는다.

3.박싱/언박싱(Boxing/Un-Boxing) (참고: http://hongjinhyeon.tistory.com/90을 사용한다. 



즉, 제네릭을 이용하지 않고 Object를 사용하기 때문에 모든 데이터 타입을 다 받고 처리 할 수 있는 장점이 있지만

자료의 입력에 내부적으로 박싱이 일어나고 사용하는 곳에서도 다시 언박싱을 해줘야 사용이 가능합니다.



2.딕셔너리 ( Dictionary )


//생성- 제네릭 기반 
Dictionary<string, HongsClass> dictionary = new Dictionary<string, HongsClass>();

//자료추가
dictionary.Add("Data1", new HongsClass() { Name = "홍진현1", intCount = 1 });
dictionary.Add("Data2", new HongsClass() { Name = "홍진현2", intCount = 2 });

//자료검색
if (dictionary.ContainsKey("Data1").Equals(true))
{
    Console.WriteLine(dictionary["Data1"].Name);
}

//Loop 전체 순회출력
foreach (HongsClass NowData in dictionary.Values)
{
    Console.WriteLine(NowData.Name);
}

//결과
//홍진현1
//홍진현1
//홍진현2


[딕셔너리의 특징]

1.Generic
2.Key와 Value 모두 Strong Type을 입력받는다.(선언시 타입을 입력해줘야함)

3.박싱/언박싱이 일어나지 않는다.



+딕셔너리는 선언시에 사용할 타입을 미리 설정하기 때문에 입력시나 출력시에도 박싱/언박싱이 일어나지 않습니다.

따라서 입력과 사용에 용이하며, 외부에서도 이 타입을 사용할 때도 타입이 정의되어 있으니 다른 타입으로 형변환을

시도하다가 실패할 염려가 없습니다.



3.결론


+두가지 타입이 사용법은 비슷하지만 내부적인 처리와 수용하는 타입의 형태가 다르므로 필요에 따라서 선택을 해야합니다.

고정적으로 하나의 타입만 입력받을 시에는 딕셔너리를 사용하며, Value에 일정한 형식이 없고 여러 형태를 저장하려면

해시테이블을 사용해야합니다.




And

[C#] 리플렉션 ( Reflection )

|

리플렉션(Reflection)이란 ?


MSDN에서는  아래와 같이 설명합니다.


리플렉션에서는 어셈블리, 모듈 및 형식을 설명하는 Type 형식의 개체를 제공합니다.

리플렉션을 사용하여 형식의 인스턴스를 동적으로 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져오고 해당 메서드를

호출하거나 해당 필드와 속성에 액세스할 수 있습니다. 코드에서 특성을 사용하는 경우 리플렉션을 통해 해당 특성에 액세스할 수 있습니다. 


+원래 코드는 어셈블러에 의해서 기계어로 바뀌면서 해당 타입정보는 사라지는것인데 C#에서는 컴파일된 실행파일에 타입에대한

 메타정보가 저장됩니다.  따라서 실행중에도 해당 타입에 대한 정보를 불러와서 사용이 가능합니다.


기본적으로는 해당 타입에 대해서 GetMethods() 함수를 이용해서 포함된 메서드들을 리스트로 가져 올 수있으며 

GetFields()로 속한 멤버변수들을 가져올 수가 있다.



1.클래스 정보
namespace TistoryWin
{
    class HongsClass
    {
        public string Name { get; set; }
        public int intCount { get; set; }

        public void NameToString()
        {
            Console.WriteLine(Name);
        }

        public void CountTostring()
        {
            Console.WriteLine(intCount.ToString());
        }
    }
}

각각 멤버 변수 2개와 메서드가 2개가 선언 되어 있을 때 아래처럼 불러오면 해당 메서드와 멤버변수들을 불러 올 수가 있다.



2.리플렉션을 이용해서 타입정보와 메서드 정보, 멤버변수 정보를 불러오기.
 

 Type t = Type.GetType("TistoryWin.HongsClass");

 foreach (MethodInfo item in t.GetMethods())
 {
     Console.WriteLine(item.ToString());
 }

 foreach (PropertyInfo item in t.GetProperties())
 {
     Console.WriteLine(item.ToString());
 }

단, 위의 GetMethods()는 2개 이상이 출력되는데 기본적으로 상속받은 메서드들도 다 출력이 됩니다.




3.리플렉션을 이용하면 아래와 같은상황에서 매우 편리하게 클래스를 초기화 할 수 있습니다.


만약, 인터넷에서 REST로 다운로드 되는 XML에서 하나의 정보를 구성하는 데이터가 수십개의 노드들로 구성이 되어있고 

이에 해당하는 클래스를 멤버변수를 노드의 이름으로 설정을 해놓은 상황이있다고 했을때, 

Node들의 이름에 따라서 멤버변수을 설정해야 할 때는 일일이 지정하기가 힘듭니다. 이때 리플렉션을 이용하면 한줄로

깔끔하게 처리가 됩니다.

   

 foreach (XmlNode nowItem in DataItems)
 {
     tempStatus = new HongsClass();
     foreach (XmlNode nowSubItem in nowItem)
     {
         tempStatus.GetType().GetProperty(nowSubItem.Name).SetValue(tempStatus, nowSubItem.InnerText, null);
     }

     ReturnData.Add(tempStatus);

 }

위에서 우선 GetProperty로 해당 노드의 이름에 대한 프로퍼니가 어떤것인지를 가져왔으며, 두번째로 SetValue()라는 함수를 이용해서

tempStatus의 인스턴스에 해당멤버변수에 값을 nowSubItem.InnerText로 설정하는 내용입니다.





And